summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorchenzizhan <[email protected]>2024-07-26 18:02:29 +0800
committerchenzizhan <[email protected]>2024-07-26 18:02:29 +0800
commit240bbbb0e0409a6bca409eb319a0a00960cbc6fb (patch)
treeb15ddd24d6b851ccf797a5919df52d7994e4dbc9 /src
parent3f46275f81d2d5af416f27fb24ab2c5ac21ec418 (diff)
parent97e8724310c1a0d51600d723c6d3dcb6c4495d5f (diff)
Merge branch 'refactor-heavykeeper-newkey' into develop-version4
Diffstat (limited to 'src')
-rw-r--r--src/cells/hash_table.c233
-rw-r--r--src/cells/hash_table.h30
-rw-r--r--src/cells/heavy_keeper.c825
-rw-r--r--src/cells/heavy_keeper.h48
-rw-r--r--src/cells/hll_common.c384
-rw-r--r--src/cells/hll_common.h28
-rw-r--r--src/cells/spread_sketch.c719
-rw-r--r--src/cells/spread_sketch.h55
-rw-r--r--src/cube.c1393
-rw-r--r--src/cube.h71
-rw-r--r--src/exdata.h7
-rw-r--r--src/exporter/cjson_exporter.c407
-rw-r--r--src/exporter/fieldstat_exporter.py18
-rw-r--r--src/fieldstat.c1409
-rw-r--r--src/fieldstat_easy.c70
-rw-r--r--src/metrics/hyperloglog.c (renamed from src/metrics/st_hyperloglog.c)134
-rw-r--r--src/metrics/hyperloglog.h (renamed from src/metrics/st_hyperloglog.h)32
-rw-r--r--src/metrics/metric.c756
-rw-r--r--src/metrics/metric.h39
-rw-r--r--src/metrics/metric_manifest.c134
-rw-r--r--src/metrics/metric_manifest.h42
-rw-r--r--src/metrics/python_api.c16
-rw-r--r--src/tags/cell_manager.c399
-rw-r--r--src/tags/cell_manager.h41
-rw-r--r--src/tags/heavy_keeper.c563
-rw-r--r--src/tags/heavy_keeper.h110
-rw-r--r--src/tags/my_ut_hash.c467
-rw-r--r--src/tags/my_ut_hash.h32
-rw-r--r--src/tags/my_ut_hash_inner.h38
-rw-r--r--src/tags/sorted_set.c464
-rw-r--r--src/tags/sorted_set.h89
31 files changed, 4615 insertions, 4438 deletions
diff --git a/src/cells/hash_table.c b/src/cells/hash_table.c
new file mode 100644
index 0000000..255a3ef
--- /dev/null
+++ b/src/cells/hash_table.c
@@ -0,0 +1,233 @@
+#include "hash_table.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "uthash.h"
+
+#include "fieldstat.h"
+#include "exdata.h"
+
+struct entry {
+ char *key;
+ size_t key_len;
+ void *exdata;
+ bool dying;
+ UT_hash_handle hh;
+};
+
+struct hash_table {
+ struct entry *key_exdata_map;
+ int current_cell_num;
+ int max_cell_num;
+
+ exdata_new_cb new_fn;
+ exdata_free_cb free_fn;
+ exdata_merge_cb merge_fn;
+ exdata_reset_cb reset_fn;
+ exdata_copy_cb copy_fn;
+};
+static void *default_new_fn(void *arg) {
+ return NULL;
+}
+
+static void default_free_fn(void *exdata) {
+ return;
+}
+
+static void default_merge_fn(void *dest, void *src) {
+ return;
+}
+
+static void default_reset_fn(void *exdata) {
+ return;
+}
+
+static void *default_copy_fn(void *exdata) {
+ return exdata;
+}
+
+
+struct hash_table *hash_table_new(int max_query_num) {
+ struct hash_table *pthis = calloc(1, sizeof(struct hash_table));
+
+ pthis->max_cell_num = max_query_num;
+
+ pthis->new_fn = default_new_fn;
+ pthis->free_fn = default_free_fn;
+ pthis->merge_fn = default_merge_fn;
+ pthis->reset_fn = default_reset_fn;
+ pthis->copy_fn = default_copy_fn;
+ return pthis;
+}
+
+void hash_table_free(struct hash_table *pthis) {
+ if (pthis == NULL) {
+ return;
+ }
+ struct entry *item, *tmp;
+ HASH_ITER(hh, pthis->key_exdata_map, item, tmp) {
+ HASH_DEL(pthis->key_exdata_map, item);
+ free(item->key);
+ pthis->free_fn(item->exdata);
+ free(item);
+ }
+ free(pthis);
+}
+
+void hash_table_reset(struct hash_table *pthis) {
+ struct entry *node, *tmp;
+ HASH_ITER(hh, pthis->key_exdata_map, node, tmp) {
+ if (!node->dying) {
+ node->dying = true;
+ pthis->reset_fn(node->exdata);
+ continue;
+ }
+ HASH_DEL(pthis->key_exdata_map, node);
+ free(node->key);
+ pthis->free_fn(node->exdata);
+ free(node);
+ }
+
+ pthis->current_cell_num = 0;
+}
+
+static char *my_keydup(const char *key, size_t key_len) {
+ char *ret = calloc(1, key_len + 1);
+ memcpy(ret, key, key_len);
+ return ret;
+}
+
+int hash_table_add(struct hash_table *pthis, const char *key, size_t key_len, void *arg) {
+ struct entry *item;
+ HASH_FIND(hh, pthis->key_exdata_map, key, key_len, item);
+ if (item != NULL && !item->dying) {
+ return 1;
+ }
+
+ if (pthis->current_cell_num >= pthis->max_cell_num) {
+ return 0;
+ }
+
+ if (item != NULL) {
+ assert(item->dying);
+ item->dying = false;
+ pthis->current_cell_num++;
+ return 1;
+ }
+
+ item = calloc(1, sizeof(struct entry));
+ item->key = my_keydup(key, key_len);
+ item->key_len = key_len;
+ item->exdata = pthis->new_fn(arg);
+ item->dying = false;
+ HASH_ADD_KEYPTR(hh, pthis->key_exdata_map, item->key, key_len, item);
+
+ pthis->current_cell_num++;
+ return 1;
+}
+
+void hash_table_set_exdata_schema(struct hash_table *pthis, exdata_new_cb new_fn, exdata_free_cb free_fn, exdata_merge_cb merge_fn, exdata_reset_cb reset_fn, exdata_copy_cb copy_fn) {
+ pthis->new_fn = new_fn;
+ pthis->free_fn = free_fn;
+ pthis->merge_fn = merge_fn;
+ pthis->reset_fn = reset_fn;
+ pthis->copy_fn = copy_fn;
+}
+
+void *hash_table_get0_exdata(struct hash_table *pthis, const char *key, size_t key_len) {
+ struct entry *item;
+ HASH_FIND(hh, pthis->key_exdata_map, key, key_len, item);
+ if (item == NULL || item->dying) {
+ return NULL;
+ }
+ return item->exdata;
+}
+
+int hash_table_get_count(const struct hash_table *pthis) {
+ return pthis->current_cell_num;
+}
+
+size_t hash_table_list(const struct hash_table *pthis, void **exdatas, size_t n_exdatas) {
+ size_t actual_len = pthis->current_cell_num;
+ if (actual_len == 0) {
+ return 0;
+ }
+
+ struct entry *item, *tmp;
+ size_t i = 0;
+ HASH_ITER(hh, pthis->key_exdata_map, item, tmp) {
+ if (item->dying) {
+ continue;
+ }
+ if (i >= n_exdatas) {
+ break;
+ }
+ exdatas[i] = item->exdata;
+ i++;
+ }
+ return actual_len < n_exdatas ? actual_len : n_exdatas;
+}
+
+int hash_table_merge(struct hash_table *dest, struct hash_table *src) {
+ struct entry *item_src, *tmp;
+ struct entry *item_dst;
+ HASH_ITER(hh, src->key_exdata_map, item_src, tmp) {
+ if (item_src->dying) {
+ continue;
+ }
+
+ HASH_FIND(hh, dest->key_exdata_map, item_src->key, item_src->key_len, item_dst);
+ if (item_dst != NULL && !item_dst->dying) {
+ dest->merge_fn(item_dst->exdata, item_src->exdata);
+ continue;
+ }
+
+ if (dest->current_cell_num >= dest->max_cell_num) {
+ continue; // cannot add more cells
+ }
+
+ if (item_dst == NULL) {
+ item_dst = calloc(1, sizeof(struct entry));
+ item_dst->key = my_keydup(item_src->key, item_src->key_len);
+ item_dst->key_len = item_src->key_len;
+ item_dst->dying = false;
+ item_dst->exdata = dest->copy_fn(item_src->exdata);
+ HASH_ADD_KEYPTR(hh, dest->key_exdata_map, item_dst->key, item_dst->key_len, item_dst);
+ dest->current_cell_num++;
+ } else {
+ assert(item_dst->dying);
+ item_dst->dying = false;
+ dest->merge_fn(item_dst->exdata, item_src->exdata); //item_dst->exdata should be empty, but just merge to it.
+ dest->current_cell_num++;
+ }
+ }
+ return 0;
+}
+
+struct hash_table *hash_table_copy(const struct hash_table *src) {
+ struct hash_table *pthis = calloc(1, sizeof(struct hash_table));
+ pthis->max_cell_num = src->max_cell_num;
+ pthis->current_cell_num = src->current_cell_num;
+ pthis->new_fn = src->new_fn;
+ pthis->free_fn = src->free_fn;
+ pthis->merge_fn = src->merge_fn;
+ pthis->reset_fn = src->reset_fn;
+ pthis->copy_fn = src->copy_fn;
+
+ struct entry *item, *tmp;
+ HASH_ITER(hh, src->key_exdata_map, item, tmp) {
+ if (item->dying) {
+ continue;
+ }
+ struct entry *new_item = calloc(1, sizeof(struct entry));
+ new_item->key = my_keydup(item->key, item->key_len);
+ new_item->key_len = item->key_len;
+ new_item->exdata = pthis->copy_fn(item->exdata);
+ new_item->dying = false;
+ HASH_ADD_KEYPTR(hh, pthis->key_exdata_map, new_item->key, new_item->key_len, new_item);
+ }
+ return pthis;
+}
diff --git a/src/cells/hash_table.h b/src/cells/hash_table.h
new file mode 100644
index 0000000..59fc8e8
--- /dev/null
+++ b/src/cells/hash_table.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#include "exdata.h"
+
+struct hash_table;
+
+struct hash_table *hash_table_new(int max_query_num);
+void hash_table_set_exdata_schema(struct hash_table *pthis, exdata_new_cb new_fn, exdata_free_cb free_fn, exdata_merge_cb merge_fn, exdata_reset_cb reset_fn, exdata_copy_cb copy_fn);
+void hash_table_free(struct hash_table *pthis);
+void hash_table_reset(struct hash_table *pthis);
+int hash_table_merge(struct hash_table *dest, struct hash_table *src);
+struct hash_table *hash_table_copy(const struct hash_table *src);
+
+// int hash_table_add(struct hash_table *pthis, const char *key, size_t key_len, int count, void *arg);
+int hash_table_add(struct hash_table *pthis, const char *key, size_t key_len, void *arg);
+
+// void *hash_table_get0_exdata(struct hash_table *pthis, const char *key, size_t key_len);
+void *hash_table_get0_exdata(struct hash_table *pthis, const char *key, size_t key_len);
+int hash_table_get_count(const struct hash_table *pthis);
+size_t hash_table_list(const struct hash_table *pthis, void **exdatas, size_t n_exdatas);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/src/cells/heavy_keeper.c b/src/cells/heavy_keeper.c
new file mode 100644
index 0000000..c62e550
--- /dev/null
+++ b/src/cells/heavy_keeper.c
@@ -0,0 +1,825 @@
+#include "heavy_keeper.h"
+
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "minheap/heap.h"
+#include "mpack/mpack.h"
+#include "xxhash/xxhash.h"
+// XXHASH is faster
+#define HASH_FUNCTION(keyptr, keylen, hashv) \
+ do { \
+ hashv = XXH3_64bits(keyptr, keylen); \
+ } while (0)
+
+#include "uthash.h"
+
+#include "exdata.h"
+
+#define FP_HASH_KEY 0
+#define NOT_FIND (-1)
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+struct entry_data { // the value constitute of a sorted set entry
+ char *key;
+ size_t key_len;
+
+ void *exdata;
+};
+
+struct sorted_set {
+ heap *heap;
+ struct hash_node *hash_tbl;
+
+ int n_living_entry;
+
+ exdata_new_cb new_fn;
+ exdata_free_cb free_fn;
+ exdata_merge_cb merge_fn;
+ exdata_reset_cb reset_fn;
+ exdata_copy_cb copy_fn;
+};
+
+struct hash_node {
+ const char *key; // value is equal to entry_data::key(point to the same memory)
+ size_t key_len;
+ heap_entry *val;
+ UT_hash_handle hh;
+};
+
+struct Bucket {
+ uint64_t finger_print;
+ long long count; // the actual count, sum of all uuid_hash_node->count
+};
+
+// Parameters used in algorithm
+struct heavy_keeper_options{
+ int r; // the size of the array. Default value: 4
+ // Set it by how accurate you want. Value of 4 guarantees an accuracy more than 90% and around 97% in all tests.
+ // Not too big because it have an impact on both time and memory efficiency.
+ int w; // M2, the maximum number of buckets every array keeps. Default value: k*log(k) and no less than 100.
+ // Basically, as long as big enough, it won't affect the accuracy significantly.
+ double b; // b, bigger variance of flow size is(elephant flows take more ratio), smaller it should be.
+ // Must bigger than 1. Better not bigger than 1.3, otherwise some elephant flow will be missed.
+};
+
+struct heavy_keeper {
+ int K;
+ struct sorted_set *top_K_heap;
+ struct heavy_keeper_options params;
+ struct Bucket *sketch;
+
+ unsigned int rand_state;
+
+ exdata_new_cb new_fn;
+ exdata_free_cb free_fn;
+ exdata_merge_cb merge_fn;
+ exdata_reset_cb reset_fn;
+ exdata_copy_cb copy_fn;
+};
+
+static void *default_new_fn(void *arg) {
+ return NULL;
+}
+
+static void default_free_fn(void *exdata) {
+ return;
+}
+
+static void default_merge_fn(void *dest, void *src) {
+ return;
+}
+
+static void default_reset_fn(void *exdata) {
+ return;
+}
+
+static void *default_copy_fn(void *exdata) {
+ return exdata;
+}
+
+struct entry_data *entry_data_construct(const char *key, size_t key_len, void *exdata)
+{
+ struct entry_data *entry_data = (struct entry_data *)malloc(sizeof(struct entry_data));
+
+ entry_data->key = (char *)malloc(key_len);
+ memcpy(entry_data->key, key, key_len);
+ entry_data->key_len = key_len;
+ entry_data->exdata = exdata;
+ return entry_data;
+}
+
+void entry_data_destroy(struct entry_data *entry_data, exdata_free_cb free_fn)
+{
+ if (entry_data == NULL) {
+ return;
+ }
+ free(entry_data->key);
+ free_fn(entry_data->exdata);
+ free(entry_data);
+}
+
+static inline long long sorted_set_entry_get_score(const heap_entry *entry)
+{
+ long long score = *(long long *)entry->key;
+ if (score == 0) {
+ return 0;
+ }
+ return score - 1; // sorted set will let the count start from 1, 0 for dying entry.
+}
+
+struct entry_data *sorted_set_entry_get_data(const heap_entry *entry)
+{
+ return entry->value;
+}
+
+/*
+ dying: To reduce the time of HASH_ADD, and to support merely recording a key without any value, give a dying status.
+*/
+static inline bool sorted_set_entry_dying(const heap_entry *entry)
+{
+ long long score = *(long long *)entry->key;
+ return score == 0;
+}
+
+static inline long long safe_add(long long a, long long b)
+{
+ if (INT64_MAX - a < b) {
+ return INT64_MAX;
+ }
+ return a + b;
+}
+
+// cppcheck-suppress [constParameterCallback, unmatchedSuppression]
+static bool cmp_entry_cb(void *aa, void *bb)
+{
+ heap_entry *a = (heap_entry *)aa;
+ heap_entry *b = (heap_entry *)bb;
+ long long score_a = *(long long *)a->key;
+ long long score_b = *(long long *)b->key;
+
+ if (score_a < score_b) {
+ return true;
+ }
+ return false;
+}
+
+static int entry_get_index_cb(void *a)
+{
+ return ((heap_entry *)a)->index;
+}
+
+void entry_set_index_cb(int index, void *a)
+{
+ ((heap_entry *)a)->index = index;
+}
+
+struct sorted_set *sorted_set_new(int set_size)
+{
+ struct sorted_set *ret = (struct sorted_set *)malloc(sizeof(struct sorted_set));
+ ret->hash_tbl = NULL;
+ ret->n_living_entry = 0;
+ heap *h = (heap *)malloc(sizeof(heap));
+ init_heap(h, set_size, cmp_entry_cb, entry_get_index_cb, entry_set_index_cb);
+
+ ret->heap = h;
+
+ ret->new_fn = default_new_fn;
+ ret->free_fn = default_free_fn;
+ ret->merge_fn = default_merge_fn;
+ ret->reset_fn = default_reset_fn;
+ ret->copy_fn = default_copy_fn;
+
+ return ret;
+}
+
+void sorted_set_schema_set(struct sorted_set *ss, exdata_new_cb new_fn, exdata_free_cb free_fn, exdata_merge_cb merge_fn, exdata_reset_cb reset_fn, exdata_copy_cb copy_fn)
+{
+ ss->new_fn = new_fn;
+ ss->free_fn = free_fn;
+ ss->merge_fn = merge_fn;
+ ss->reset_fn = reset_fn;
+ ss->copy_fn = copy_fn;
+}
+
+void sorted_set_free(struct sorted_set *ss)
+{
+ struct hash_node *tbl = ss->hash_tbl;
+ struct hash_node *h_node, *tmp;
+ HASH_ITER(hh, tbl, h_node, tmp) {
+ HASH_DEL(tbl, h_node);
+ free(h_node);
+ }
+
+ heap *heap = ss->heap;
+ heap_entry *node;
+ for (int i = 0; i < heap->cur_size; i++) {
+ node = (heap_entry *)heap->nodes[i];
+ entry_data_destroy(node->value, ss->free_fn);
+ free(node->key);
+ free(node);
+ }
+ free(heap->nodes);
+ free(heap);
+
+ free(ss);
+}
+
+heap_entry *sorted_set_find_entry(const struct sorted_set *ss, const char *key, size_t key_len)
+{
+ struct hash_node *hash_tbl = ss->hash_tbl;
+ struct hash_node *s = NULL;
+ HASH_FIND(hh, hash_tbl, key, key_len, s);
+
+ if (s == NULL) {
+ return NULL;
+ }
+ return (heap_entry *)s->val;
+}
+
+static inline void sorted_set_entry_set_score(struct sorted_set *ss, heap_entry *entry, long long score)
+{
+ *(long long *)(entry->key) = score;
+
+ adjust_heap_node(ss->heap, entry);
+}
+
+int sorted_set_pop(struct sorted_set *ss)
+{
+ struct hash_node *hash_tbl = ss->hash_tbl;
+ heap_entry *entry = (heap_entry *)pop_heap(ss->heap);
+ if (entry == NULL) {
+ return -1;
+ }
+ struct entry_data *tmp_data = sorted_set_entry_get_data(entry);
+
+ struct hash_node *s = NULL;
+ HASH_FIND(hh, hash_tbl, tmp_data->key, tmp_data->key_len, s);
+ assert(s!=NULL);
+
+ HASH_DEL(hash_tbl, s);
+ free(s);
+ ss->hash_tbl = hash_tbl;
+
+ int ret;
+ if (sorted_set_entry_dying(entry)) {
+ ret = 0;
+ } else {
+ ss->n_living_entry--;
+ ret = 1;
+ }
+
+ entry_data_destroy(tmp_data, ss->free_fn);
+ free(entry->key);
+ free(entry);
+
+ return ret;
+}
+
+static inline bool sorted_set_check_is_full(const struct sorted_set *ss)
+{
+ return ss->heap->cur_size >= ss->heap->max_size;
+}
+
+long long sorted_set_get_min_score(const struct sorted_set *ss)
+{
+ heap *heap = ss->heap;
+ if (heap->cur_size == 0) {
+ return NOT_FIND;
+ }
+ const heap_entry *ret = (heap_entry *)(heap->nodes[0]);
+
+ long long score = *(long long *)ret->key;
+ if (score == 0) {
+ return NOT_FIND;
+ }
+ return score - 1; // sorted set will let the score start from 1, 0 for dying entry.
+}
+
+void sorted_set_insert_to_available_heap(struct sorted_set *ss, const char *key, size_t key_len, long long cnt, void *exdata)
+{
+ cnt = safe_add(cnt, 1); // sorted set will let the score start from 1, 0 for dying entry.
+ long long *tmp_cnt = (long long*)malloc(sizeof(long long));
+ *tmp_cnt = cnt;
+ struct entry_data *tmp_data = entry_data_construct(key, key_len, exdata);
+ heap_entry *node = (heap_entry *)malloc(sizeof(heap_entry));
+ node->key = tmp_cnt;
+ node->value = tmp_data;
+
+ push_heap(ss->heap, (void *)node);
+
+ struct hash_node *hash_tbl = ss->hash_tbl;
+ struct hash_node *s = (struct hash_node *)malloc(sizeof(struct hash_node));
+ s->key = sorted_set_entry_get_data(node)->key;
+ s->key_len = key_len;
+ s->val = node;
+ HASH_ADD_KEYPTR(hh, hash_tbl, s->key, key_len, s);
+ ss->hash_tbl = hash_tbl;
+
+ ss->n_living_entry++;
+}
+
+int sorted_set_insert(struct sorted_set *ss, const char *key, size_t key_len, long long cnt, void *args)
+{
+ // if there is a dying record, reborn it to use.
+ heap_entry *entry = sorted_set_find_entry(ss, key, key_len);
+ if (entry != NULL) {
+ if (!sorted_set_entry_dying(entry)) {
+ assert(0);
+ return -1;
+ }
+ sorted_set_entry_set_score(ss, entry, safe_add(cnt, 1)); // sorted set will let the score start from 1, 0 for dying entry.
+
+ ss->n_living_entry++;
+ return 1;
+ }
+
+ if (sorted_set_check_is_full(ss)) {
+ long long tmp_mincnt = sorted_set_get_min_score(ss);
+ if (tmp_mincnt != NOT_FIND && cnt <= tmp_mincnt) { // even if all cells in sorted set are dying, the sorted set can still be a full one, in which case, the min score is invalid.
+ return 0;
+ }
+ sorted_set_pop(ss);
+ }
+ sorted_set_insert_to_available_heap(ss, key, key_len, cnt, ss->new_fn(args));
+ return 1;
+}
+
+static inline int sorted_set_cardinality(const struct sorted_set *ss)
+{
+ return ss->n_living_entry;
+}
+
+long long sorted_set_get_score(const struct sorted_set *ss, const char *key, size_t key_len)
+{
+ if (sorted_set_cardinality(ss) == 0) {
+ return NOT_FIND;
+ }
+ const heap_entry *entry = sorted_set_find_entry(ss, key, key_len);
+ if (entry == NULL) {
+ return NOT_FIND;
+ }
+ if (sorted_set_entry_dying(entry)) {
+ return NOT_FIND;
+ }
+
+ return sorted_set_entry_get_score(entry);
+}
+
+int sorted_set_incrby(struct sorted_set *ss, const char *key, size_t key_len, long long score)
+{
+ heap_entry *entry = sorted_set_find_entry(ss, key, key_len);
+ if (entry == NULL) {
+ return -1;
+ }
+ long long cnt_old;
+ if (sorted_set_entry_dying(entry) == false) {
+ cnt_old = sorted_set_entry_get_score(entry);
+ cnt_old += 1; // sorted set will let the score start from 1, 0 for dying entry.
+ } else {
+ cnt_old = 0;
+ }
+ sorted_set_entry_set_score(ss, entry, safe_add(score, cnt_old));
+ return 0;
+}
+
+typedef struct {
+ long long key;
+ struct entry_data *val;
+} tmp_heap_node;
+
+int cmp_tmp_heap_node(const void *a, const void *b)
+{
+ const tmp_heap_node *aa = (tmp_heap_node *)a;
+ const tmp_heap_node *bb = (tmp_heap_node *)b;
+
+ if (aa->key < bb->key) {
+ return 1;
+ } else if (aa->key > bb->key) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+void sorted_set_dump(const struct sorted_set *ss, char **key_out, size_t *key_len_out, void **exdata_out)
+{
+ struct minheap *h = ss->heap;
+ tmp_heap_node *tmp_nodes = (tmp_heap_node *)malloc(sizeof(tmp_heap_node) * h->cur_size);
+ size_t n_living_entry = 0;
+ for (int i = 0; i < h->cur_size; i++) {
+ const heap_entry *entry = (heap_entry *)h->nodes[i];
+ if (sorted_set_entry_dying(entry)) {
+ continue;
+ }
+ tmp_nodes[n_living_entry].key = sorted_set_entry_get_score(entry);
+ tmp_nodes[n_living_entry].val = sorted_set_entry_get_data(entry);
+ n_living_entry++;
+ }
+
+ assert(n_living_entry == ss->n_living_entry);
+ qsort(tmp_nodes, n_living_entry, sizeof(tmp_heap_node), cmp_tmp_heap_node);
+ for (int i = 0; i < n_living_entry; i++) {
+ key_out[i] = tmp_nodes[i].val->key;
+ key_len_out[i] = tmp_nodes[i].val->key_len;
+ exdata_out[i] = tmp_nodes[i].val->exdata;
+ }
+ free(tmp_nodes);
+}
+
+struct sorted_set *sorted_set_copy(const struct sorted_set *ss)
+{
+ struct sorted_set *ret = sorted_set_new(ss->heap->max_size);
+ sorted_set_schema_set(ret, ss->new_fn, ss->free_fn, ss->merge_fn, ss->reset_fn, ss->copy_fn);
+
+ for (int i = 0; i < ss->heap->cur_size; i++) {
+ const heap_entry *entry = (heap_entry *)ss->heap->nodes[i];
+ if (sorted_set_entry_dying(entry)) {
+ continue;
+ }
+ const struct entry_data *data = sorted_set_entry_get_data(entry);
+ sorted_set_insert_to_available_heap(ret, data->key, data->key_len, sorted_set_entry_get_score(entry), ss->copy_fn(data->exdata));
+ }
+
+ return ret;
+}
+
+// set all the entry to dying status. They will be the first to be popped.
+void sorted_set_reset(struct sorted_set *ss)
+{
+ heap *heap = ss->heap;
+ heap_entry *entry;
+ for (int i = 0; i < heap->cur_size; i++) {
+ entry = (heap_entry *)heap->nodes[i];
+ *(long long *)entry->key = 0;
+
+ ss->reset_fn(sorted_set_entry_get_data(entry)->exdata);
+ }
+
+ ss->n_living_entry = 0;
+}
+
+/* -------------------------------------------------------------------------- */
+/* heavy keeper */
+/* -------------------------------------------------------------------------- */
+
+struct Bucket *new_sketch(struct heavy_keeper_options *params) {
+ size_t array_len = (size_t)params->r * (size_t)params->w;
+
+ struct Bucket *ret = (struct Bucket *)calloc(array_len, sizeof(struct Bucket));
+
+ return ret;
+}
+
+void params_set_to_default(struct heavy_keeper_options *p, int K) {
+ if (K > 5000) { // don't let the sketch too large when K gets insanely large
+ K = 5000;
+ }
+ double log_ret = log((double)K) / 2.3; // 2.3: log_e(10), log_ret = log_10(K)
+
+ if (log_ret < 3) {
+ p->r = 3;
+ } else {
+ p->r = (int)(log_ret);
+ }
+ p->b = 1.17; // by test, 1.17 is the best
+ p->w = (int)(log_ret * K * 2);
+ if (p->w < 150) {
+ p->w = 150; // determined through test, too small max_bucket_num will let accuracy decrease severely.
+ } else if (p->w > 600) {
+ p->w = p->w / 4 + 450;
+ }
+}
+
+struct heavy_keeper *heavy_keeper_new(int max_query_num) {
+ struct heavy_keeper *hk = (struct heavy_keeper *)malloc(sizeof(struct heavy_keeper));
+ hk->K = max_query_num;
+ hk->rand_state = 0;
+
+ hk->top_K_heap = sorted_set_new(max_query_num);
+
+ params_set_to_default(&(hk->params), max_query_num);
+
+ hk->sketch = new_sketch(&(hk->params));
+
+ hk->new_fn = default_new_fn;
+ hk->free_fn = default_free_fn;
+ hk->merge_fn = default_merge_fn;
+ hk->reset_fn = default_reset_fn;
+
+ return hk;
+}
+
+void heavy_keeper_free(struct heavy_keeper *hk) {
+ if (hk == NULL) {
+ return;
+ }
+ sorted_set_free(hk->top_K_heap);
+ free(hk->sketch);
+
+ free(hk);
+}
+
+void heavy_keeper_reset(struct heavy_keeper *hk) {
+ sorted_set_reset(hk->top_K_heap);
+ memset(hk->sketch, 0, sizeof(struct Bucket) * (size_t)hk->params.r * (size_t)hk->params.w);
+}
+
+const int DECAY_POW_TABLE[128] = { // 1.17 ^ exp * RAND_MAX, exp is in [0,127]
+2147483647,1835456109,1568765905,1340825560,1146004752,979491241,837172001,715531625,611565491,522705548,446756879,381843486,326361954,278941841,238411830,203770795,174163072,148857327,127228484,108742294,92942132,
+79437720,67895487,58030331,49598573,42391943,36232430,30967889,26468281,22622462,19335438,16526015,14124799,12072478,10318357,8819109,7537700,6442479,5506392,4706318,4022494,3438029,2938486,2511527,2146604,
+1834704,1568123,1340276,1145535,979090,836829,715239,611315,522491,446574,381687,326228,278828,238314,203687,174092,148796,127176,108698,92904,79405,67868,58007,49578,42375,36218,30955,26457,22613,19328,16519,
+14119,12068,10314,8815,7535,6440,5504,4704,4021,3437,2937,2510,2146,1834,1567,1340,1145,979,836,715,611,522,446,382,326,279,238,204,174,149,127,109,93,79,68,58,50,42,36,31,26,23,19,17,14,12,10,9,8,6,6,5,
+};
+bool if_need_to_decay(struct heavy_keeper *hk, const struct Bucket *bucket, long long count) {
+ if (count == 0) {
+ return false;
+ }
+ if (bucket->count < count) { // the exp is 0, so the decay rate is 1
+ return true;
+ }
+
+ long long exp = bucket->count / count;
+ if (exp > 127) { // decay_rate too small, almost no chance to decay
+ return false;
+ }
+
+ // double decay_rate = pow(hk->params.b, -exp);
+ // p->b = 1.17 is fixed, search table to get result directly.
+ int decay_rate = DECAY_POW_TABLE[exp];
+
+ if (rand_r(&(hk->rand_state)) < decay_rate) {
+ return true;
+ }
+ return false;
+}
+
+static inline uint64_t cal_hash_val_with_seed(const char *key, size_t key_len, unsigned int seed) {
+ return XXH3_64bits_withSeed(key, key_len, seed);
+}
+
+/*
+1 for newly add something. 0 for not add. -1 for unexpected cases.
+*/
+int heavy_keeper_add(struct heavy_keeper *heavy_keeper, const char *key, size_t key_len, long long count, void *arg) {
+ assert(count >= 0);
+ if (count == 0) {
+ if (sorted_set_cardinality(heavy_keeper->top_K_heap) < heavy_keeper->K) {
+ const struct heap_entry *entry = sorted_set_find_entry(heavy_keeper->top_K_heap, key, key_len);
+
+ if (entry != NULL && !sorted_set_entry_dying(entry)) {
+ return 0;
+ }
+
+ sorted_set_insert(heavy_keeper->top_K_heap, key, key_len, count, arg);
+ return 1;
+ }
+ return 0;
+ }
+
+ struct sorted_set *summary = heavy_keeper->top_K_heap;
+
+ long long old_cnt = sorted_set_get_score(summary, key, key_len);
+ bool not_in_sorted_set = (old_cnt == NOT_FIND);
+ long long maxv = 0;
+ uint64_t fp = cal_hash_val_with_seed(key, key_len, FP_HASH_KEY);
+ uint64_t h1 = fp;
+ uint64_t h2 = cal_hash_val_with_seed(key, key_len, FP_HASH_KEY+1);
+
+ for (uint64_t j = 0; j < heavy_keeper->params.r; j++) {
+ uint64_t hashv = h1 + j * h2; // use `double hashing` strategy
+ struct Bucket *bucket = &(heavy_keeper->sketch[j * heavy_keeper->params.w + (hashv % heavy_keeper->params.w)]);
+
+ if (bucket->finger_print == fp) {
+ // If a key is not in the min-heap, then the estimated key size should be no larger than nmin.
+ // or if the min-heap is not full(min_value == NOT_FIND), every key should be taken into account, so of course it should be added.
+ // in neither case, bucket->count > nMin && not_in_sorted_set happen.
+ // The keys whose counts are both larger than nmin and not in min-heap must have the same xxhash value, and its FP stored in bucket represents another field,
+ // In this case, the sketch won't be updated. This key is expected to be taken into account in another array,
+ // where its FP is different from the one it should collided with, so that element keys won't be missed.
+ if (not_in_sorted_set) {
+ long long min_value = sorted_set_get_min_score(summary);
+ if (min_value != NOT_FIND && bucket->count > min_value) {
+ continue;
+ }
+ }
+ bucket->count = safe_add(bucket->count, count);
+ maxv = MAX(maxv, bucket->count);
+ } else {
+ if (!if_need_to_decay(heavy_keeper, bucket, count)) {
+ continue;
+ }
+
+ if (bucket->count < count) {
+ bucket->finger_print = fp;
+ bucket->count = count;
+
+ maxv = MAX(maxv, count);
+ } else {
+ bucket->count -= count;
+ }
+ }
+ }
+
+ if (not_in_sorted_set) {
+ if (sorted_set_cardinality(summary) != heavy_keeper->K) {
+ sorted_set_insert(summary, key, key_len, maxv, arg);
+ return 1;
+ }
+ long long min_value = sorted_set_get_min_score(summary);
+ if (maxv > min_value || min_value == NOT_FIND) {
+ sorted_set_insert(summary, key, key_len, maxv, arg);
+ return 1;
+ }
+ return 0;
+ } else {
+ if (maxv > old_cnt) {
+ sorted_set_incrby(summary, key, key_len, maxv - old_cnt);
+ }
+ return 1; // no popped, but the exdata definitely exists in the sorted set
+ }
+}
+
+int heavy_keeper_set_exdata_schema(struct heavy_keeper *hk, exdata_new_cb new_fn, exdata_free_cb free_fn, exdata_merge_cb merge_fn, exdata_reset_cb reset_fn, exdata_copy_cb copy_fn) {
+ sorted_set_schema_set(hk->top_K_heap, new_fn, free_fn, merge_fn, reset_fn, copy_fn);
+ hk->new_fn = new_fn;
+ hk->free_fn = free_fn;
+ hk->merge_fn = merge_fn;
+ hk->reset_fn = reset_fn;
+ hk->copy_fn = copy_fn;
+
+ return 0;
+}
+
+void *heavy_keeper_get0_exdata(const struct heavy_keeper *hk, const char *key, size_t key_len) {
+ const heap_entry *entry = sorted_set_find_entry(hk->top_K_heap, key, key_len);
+ if (entry == NULL || sorted_set_entry_dying(entry)) {
+ return NULL;
+ }
+ return sorted_set_entry_get_data(entry)->exdata;
+}
+
+int heavy_keeper_get_count(const struct heavy_keeper *hk) {
+ return sorted_set_cardinality(hk->top_K_heap);
+}
+
+size_t heavy_keeper_list(const struct heavy_keeper *hk, void **exdatas, size_t n_exdatas) {
+ size_t actual_len = sorted_set_cardinality(hk->top_K_heap);
+ if (actual_len == 0) {
+ return 0;
+ }
+
+ char **keys_dummy = (char **)malloc(sizeof(char *) * actual_len);
+ size_t *key_lens_dummy = (size_t *)malloc(sizeof(size_t) * actual_len);
+ if (n_exdatas < actual_len) {
+ void **exdatas_ret = (void **)malloc(sizeof(void *) * actual_len);
+ sorted_set_dump(hk->top_K_heap, keys_dummy, key_lens_dummy, exdatas_ret);
+ memcpy(exdatas, exdatas_ret, sizeof(void *) * n_exdatas);
+ free(exdatas_ret);
+ } else {
+ sorted_set_dump(hk->top_K_heap, keys_dummy, key_lens_dummy, exdatas);
+ }
+
+ free(keys_dummy);
+ free(key_lens_dummy);
+
+ return actual_len < n_exdatas ? actual_len : n_exdatas;
+}
+
+static void heavy_keeper_merge_sketch(struct heavy_keeper *dest, const struct heavy_keeper *src) {
+ int w = dest->params.w;
+ int d = dest->params.r;
+ //idx
+ for (int array_id = 0; array_id < d; array_id++) {
+ for (int bucket_id = 0; bucket_id < w; bucket_id++) {
+ struct Bucket *bucket_dest = &(dest->sketch[array_id * w + bucket_id]);
+ const struct Bucket *bucket_src = &(src->sketch[array_id * w + bucket_id]);
+
+ if (bucket_dest->finger_print == bucket_src->finger_print) {
+ bucket_dest->count = safe_add(bucket_dest->count, bucket_src->count);
+ } else {
+ if (bucket_dest->count < bucket_src->count) {
+ bucket_dest->count = bucket_src->count;
+ bucket_dest->finger_print = bucket_src->finger_print;
+ }
+ }
+ }
+ }
+}
+
+long long find_count_in_sketch(struct heavy_keeper *hk, const char *key, size_t key_len) {
+ struct Bucket *bucket;
+ uint64_t fp = cal_hash_val_with_seed(key, key_len, FP_HASH_KEY);
+ uint64_t h1 = fp;
+ uint64_t h2 = cal_hash_val_with_seed(key, key_len, FP_HASH_KEY+1);
+
+ long long maxv = 0;
+ for (uint64_t array_id = 0; array_id < hk->params.r; array_id++) {
+ uint64_t hash = h1 + array_id * h2;
+ bucket = &(hk->sketch[array_id * hk->params.w + (hash % hk->params.w)]);
+
+ if (bucket->finger_print == fp) {
+ maxv = MAX(maxv, bucket->count);
+ }
+ }
+
+ return maxv;
+}
+
+void heavy_keeper_merge(struct heavy_keeper *dest, const struct heavy_keeper *src) {
+ assert(dest->K == src->K);
+
+ heavy_keeper_merge_sketch(dest, src);
+
+ struct sorted_set *new_rec = sorted_set_new(dest->K); // merging result
+ sorted_set_schema_set(new_rec, dest->new_fn, dest->free_fn, dest->merge_fn, dest->reset_fn, dest->copy_fn);
+ const struct sorted_set *ss_dest = dest->top_K_heap; // original ss in dest
+ const struct sorted_set *ss_src = src->top_K_heap; // the ss to be merged
+ int size_dest = sorted_set_cardinality(ss_dest);
+ int size_src = sorted_set_cardinality(ss_src);
+ int max_size = size_dest > size_src ? size_dest : size_src;
+
+ void **exdatas_dst = (void **)calloc(size_dest, sizeof(void *));
+ char **key_arr = (char **)malloc(sizeof(char *) * max_size);
+ size_t *key_lens = (size_t *)malloc(sizeof(size_t) * max_size);
+ sorted_set_dump(ss_dest, key_arr, key_lens, exdatas_dst);
+
+
+ /* ------------------------------ merge dest ------------------------------ */
+ for (int i = 0; i < size_dest; i++) {
+ long long maxv = find_count_in_sketch(dest, key_arr[i], key_lens[i]);
+ sorted_set_insert_to_available_heap(new_rec, key_arr[i], key_lens[i], maxv, dest->copy_fn(exdatas_dst[i]));
+ }
+
+ /* ------------------------------ merge src ------------------------------ */
+ void **exdatas_src = (void **)calloc(size_src, sizeof(void *));
+ sorted_set_dump(ss_src, key_arr, key_lens, exdatas_src);
+
+ for (int i = 0; i < size_src; i++) {
+ const heap_entry *entry = sorted_set_find_entry(new_rec, key_arr[i], key_lens[i]);
+ if (entry != NULL) { // the key is in both dest and src, so has been processed in the previous loop. The reason why no need to sum up result is that merged sketch already keeps its summed up count
+ void *exdata_new = sorted_set_entry_get_data(entry)->exdata;
+ void *exdata_src = exdatas_src[i];
+ dest->merge_fn(exdata_new, exdata_src);
+
+ continue;
+ }
+
+ long long cnt = find_count_in_sketch(dest, key_arr[i], key_lens[i]);// the cnt is the estimated count in the merged sketch, since the dest heavy keeper has been merged
+ if (sorted_set_check_is_full(new_rec)) {
+ long long mincnt_new = sorted_set_get_min_score(new_rec);
+ if (cnt > mincnt_new) {
+ sorted_set_pop(new_rec);
+ sorted_set_insert_to_available_heap(new_rec, key_arr[i], key_lens[i], cnt, dest->copy_fn(exdatas_src[i]));
+ }
+ } else {
+ sorted_set_insert_to_available_heap(new_rec, key_arr[i], key_lens[i], cnt, dest->copy_fn(exdatas_src[i]));
+ }
+ }
+
+ free(key_arr);
+ free(key_lens);
+ free(exdatas_dst);
+ free(exdatas_src);
+ sorted_set_free(dest->top_K_heap);
+ dest->top_K_heap = new_rec;
+}
+
+struct heavy_keeper *heavy_keeper_copy(const struct heavy_keeper *src) {
+ struct heavy_keeper *ret = (struct heavy_keeper *)malloc(sizeof(struct heavy_keeper));
+ ret->K = src->K;
+ ret->rand_state = 0;
+
+ ret->top_K_heap = sorted_set_copy(src->top_K_heap);
+
+ ret->params = src->params;
+ ret->sketch = (struct Bucket *)malloc(sizeof(struct Bucket) * (size_t)ret->params.r * (size_t)ret->params.w);
+ memcpy(ret->sketch, src->sketch, sizeof(struct Bucket) * (size_t)ret->params.r * (size_t)ret->params.w);
+
+ ret->new_fn = src->new_fn;
+ ret->free_fn = src->free_fn;
+ ret->merge_fn = src->merge_fn;
+ ret->reset_fn = src->reset_fn;
+ ret->copy_fn = src->copy_fn;
+
+ return ret;
+}
+
+void heavy_keeper_one_point_query(const struct heavy_keeper *hk, const char *key, size_t key_len, long long *count_out, void **exdata_out) {
+ *count_out = 0;
+ *exdata_out = NULL;
+
+ const heap_entry *entry = sorted_set_find_entry(hk->top_K_heap, key, key_len);
+ if (entry == NULL) {
+ return;
+ }
+ *count_out = sorted_set_entry_get_score(entry);
+ *exdata_out = sorted_set_entry_get_data(entry)->exdata;
+} \ No newline at end of file
diff --git a/src/cells/heavy_keeper.h b/src/cells/heavy_keeper.h
new file mode 100644
index 0000000..3b09598
--- /dev/null
+++ b/src/cells/heavy_keeper.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#include "exdata.h"
+
+
+struct heavy_keeper;
+
+struct heavy_keeper *heavy_keeper_new(int max_query_num);
+/**
+ * @brief free a heavy keeper.
+ * @param hk the pointer to the heavy keeper.
+ */
+void heavy_keeper_free(struct heavy_keeper *hk);
+
+/*
+ * @brief clear a heavy keeper. It will clear all the data in the heavy keeper. as for every exdata, use the reset function `exdata_reset_cb` to reset it.
+ * @param hk the pointer to the heavy keeper.
+*/
+void heavy_keeper_reset(struct heavy_keeper *hk);
+
+int heavy_keeper_add(struct heavy_keeper *hk, const char *key, size_t key_len, long long count, void *arg);
+
+int heavy_keeper_set_exdata_schema(struct heavy_keeper *hk, exdata_new_cb new_fn, exdata_free_cb free_fn, exdata_merge_cb merge_fn, exdata_reset_cb reset_fn, exdata_copy_cb copy_fn);
+
+void *heavy_keeper_get0_exdata(const struct heavy_keeper *hk, const char *key, size_t key_len);
+
+// get the number of cells in the heavy keeper
+int heavy_keeper_get_count(const struct heavy_keeper *hk);
+
+size_t heavy_keeper_list(const struct heavy_keeper *hk, void **exdatas, size_t n_exdatas);
+
+void heavy_keeper_merge(struct heavy_keeper *dest, const struct heavy_keeper *src);
+
+struct heavy_keeper *heavy_keeper_copy(const struct heavy_keeper *src);
+
+// for test
+void heavy_keeper_one_point_query(const struct heavy_keeper *hk, const char *key, size_t key_len, long long *count_out, void **exdata_out);
+
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/src/cells/hll_common.c b/src/cells/hll_common.c
new file mode 100644
index 0000000..47dc113
--- /dev/null
+++ b/src/cells/hll_common.c
@@ -0,0 +1,384 @@
+
+#include "hll_common.h"
+#include "crdt_utils.h"
+
+#include "base64/b64.h"
+
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#define REG_WIDTH 6 // Bits per register
+#define INT_WIDTH 32 // Bits in an int
+#define REG_PER_WORD 5 // floor(INT_WIDTH / REG_WIDTH)
+
+#define NUM_REG(precision) ((1 << precision))
+#define INT_CEIL(num, denom) (((num) + (denom) - 1) / (denom))
+
+#define PRECISION_TO_WORD(precision) INT_CEIL(NUM_REG(precision), REG_PER_WORD)
+#define RESET_TIME_SLOT_US(time_window_ms, precision) (time_window_ms*2*1000/NUM_REG(precision))
+#define REGISTER_SIZE(precision) INT_WIDTH*INT_CEIL(NUM_REG(precision), REG_PER_WORD)
+
+static int get_register(const uint32_t *registers, int idx) {
+ uint32_t word = *(registers + (idx / REG_PER_WORD));
+ word = word >> REG_WIDTH * (idx % REG_PER_WORD);
+ return word & ((1 << REG_WIDTH) - 1);
+}
+
+static void set_register(uint32_t *registers, int idx, int val) {
+ uint32_t *word = registers + (idx / REG_PER_WORD);
+
+ // Shift the val into place
+ unsigned shift = REG_WIDTH * (idx % REG_PER_WORD);
+ val = val << shift;
+ uint32_t val_mask = ((1 << REG_WIDTH) - 1) << shift;
+
+ // Store the word
+ *word = (*word & ~val_mask) | val;
+ return;
+}
+static void reset_register(uint32_t *registers, int idx)
+{
+ uint32_t *word = registers + (idx / REG_PER_WORD);
+ unsigned shift = REG_WIDTH * (idx % REG_PER_WORD);
+ uint32_t val_mask = ((1 << REG_WIDTH) - 1) << shift;
+ *word &= ~val_mask;
+}
+uint32_t *hll_create_register(int precision)
+{
+ return ALLOC(uint32_t, PRECISION_TO_WORD(precision));
+}
+uint32_t *hll_duplicate(const uint32_t *registers, unsigned char precision)
+{
+ int words = PRECISION_TO_WORD(precision);
+ uint32_t *new_registers = ALLOC(uint32_t, words);
+ memcpy(new_registers, registers, sizeof(uint32_t)*words);
+ return new_registers;
+}
+void hll_free_register(uint32_t *registers)
+{
+ free(registers);
+ return;
+}
+static void clear_register(uint32_t *registers, int precision)
+{
+ memset(registers, 0, PRECISION_TO_WORD(precision)*sizeof(uint32_t));
+ return;
+}
+int hll_add_hash(uint32_t *registers, unsigned char precision, uint64_t hash)
+{
+ // Determine the index using the first p bits
+ int idx = hash >> (64 - precision);
+
+ // Shift out the index bits
+ hash = hash << precision | (1 << (precision -1));
+
+ // Determine the count of leading zeros. The __builtin_clzll() is provided by GCC
+ int leading = __builtin_clzll(hash) + 1;
+
+ // Update the register if the new value is larger
+ if (leading > get_register(registers, idx)) {
+ set_register(registers, idx, leading);
+ return 1;
+ }
+ return 0;
+}
+
+
+static const double *g_rawEstimateData[] = {
+ // precision 4
+ (double[]) { 11, 11.717, 12.207, 12.7896, 13.2882, 13.8204, 14.3772, 14.9342, 15.5202, 16.161, 16.7722, 17.4636, 18.0396, 18.6766, 19.3566, 20.0454, 20.7936, 21.4856, 22.2666, 22.9946, 23.766, 24.4692, 25.3638, 26.0764, 26.7864, 27.7602, 28.4814, 29.433, 30.2926, 31.0664, 31.9996, 32.7956, 33.5366, 34.5894, 35.5738, 36.2698, 37.3682, 38.0544, 39.2342, 40.0108, 40.7966, 41.9298, 42.8704, 43.6358, 44.5194, 45.773, 46.6772, 47.6174, 48.4888, 49.3304, 50.2506, 51.4996, 52.3824, 53.3078, 54.3984, 55.5838, 56.6618, 57.2174, 58.3514, 59.0802, 60.1482, 61.0376, 62.3598, 62.8078, 63.9744, 64.914, 65.781, 67.1806, 68.0594, 68.8446, 69.7928, 70.8248, 71.8324, 72.8598, 73.6246, 74.7014, 75.393, 76.6708, 77.2394, },
+ // precision 5
+ (double[]) { 23, 23.1194, 23.8208, 24.2318, 24.77, 25.2436, 25.7774, 26.2848, 26.8224, 27.3742, 27.9336, 28.503, 29.0494, 29.6292, 30.2124, 30.798, 31.367, 31.9728, 32.5944, 33.217, 33.8438, 34.3696, 35.0956, 35.7044, 36.324, 37.0668, 37.6698, 38.3644, 39.049, 39.6918, 40.4146, 41.082, 41.687, 42.5398, 43.2462, 43.857, 44.6606, 45.4168, 46.1248, 46.9222, 47.6804, 48.447, 49.3454, 49.9594, 50.7636, 51.5776, 52.331, 53.19, 53.9676, 54.7564, 55.5314, 56.4442, 57.3708, 57.9774, 58.9624, 59.8796, 60.755, 61.472, 62.2076, 63.1024, 63.8908, 64.7338, 65.7728, 66.629, 67.413, 68.3266, 69.1524, 70.2642, 71.1806, 72.0566, 72.9192, 73.7598, 74.3516, 75.5802, 76.4386, 77.4916, 78.1524, 79.1892, 79.8414, 80.8798, 81.8376, 82.4698, 83.7656, 84.331, 85.5914, 86.6012, 87.7016, 88.5582, 89.3394, 90.3544, 91.4912, 92.308, 93.3552, 93.9746, 95.2052, 95.727, 97.1322, 98.3944, 98.7588, 100.242, 101.1914, 102.2538, 102.8776, 103.6292, 105.1932, 105.9152, 107.0868, 107.6728, 108.7144, 110.3114, 110.8716, 111.245, 112.7908, 113.7064, 114.636, 115.7464, 116.1788, 117.7464, 118.4896, 119.6166, 120.5082, 121.7798, 122.9028, 123.4426, 124.8854, 125.705, 126.4652, 128.3464, 128.3462, 130.0398, 131.0342, 131.0042, 132.4766, 133.511, 134.7252, 135.425, 136.5172, 138.0572, 138.6694, 139.3712, 140.8598, 141.4594, 142.554, 143.4006, 144.7374, 146.1634, 146.8994, 147.605, 147.9304, 149.1636, 150.2468, 151.5876, 152.2096, 153.7032, 154.7146, 155.807, 156.9228, 157.0372, 158.5852, },
+ // precision 6
+ (double[]) { 46, 46.1902, 47.271, 47.8358, 48.8142, 49.2854, 50.317, 51.354, 51.8924, 52.9436, 53.4596, 54.5262, 55.6248, 56.1574, 57.2822, 57.837, 58.9636, 60.074, 60.7042, 61.7976, 62.4772, 63.6564, 64.7942, 65.5004, 66.686, 67.291, 68.5672, 69.8556, 70.4982, 71.8204, 72.4252, 73.7744, 75.0786, 75.8344, 77.0294, 77.8098, 79.0794, 80.5732, 81.1878, 82.5648, 83.2902, 84.6784, 85.3352, 86.8946, 88.3712, 89.0852, 90.499, 91.2686, 92.6844, 94.2234, 94.9732, 96.3356, 97.2286, 98.7262, 100.3284, 101.1048, 102.5962, 103.3562, 105.1272, 106.4184, 107.4974, 109.0822, 109.856, 111.48, 113.2834, 114.0208, 115.637, 116.5174, 118.0576, 119.7476, 120.427, 122.1326, 123.2372, 125.2788, 126.6776, 127.7926, 129.1952, 129.9564, 131.6454, 133.87, 134.5428, 136.2, 137.0294, 138.6278, 139.6782, 141.792, 143.3516, 144.2832, 146.0394, 147.0748, 148.4912, 150.849, 151.696, 153.5404, 154.073, 156.3714, 157.7216, 158.7328, 160.4208, 161.4184, 163.9424, 165.2772, 166.411, 168.1308, 168.769, 170.9258, 172.6828, 173.7502, 175.706, 176.3886, 179.0186, 180.4518, 181.927, 183.4172, 184.4114, 186.033, 188.5124, 189.5564, 191.6008, 192.4172, 193.8044, 194.997, 197.4548, 198.8948, 200.2346, 202.3086, 203.1548, 204.8842, 206.6508, 206.6772, 209.7254, 210.4752, 212.7228, 214.6614, 215.1676, 217.793, 218.0006, 219.9052, 221.66, 223.5588, 225.1636, 225.6882, 227.7126, 229.4502, 231.1978, 232.9756, 233.1654, 236.727, 238.1974, 237.7474, 241.1346, 242.3048, 244.1948, 245.3134, 246.879, 249.1204, 249.853, 252.6792, 253.857, 254.4486, 257.2362, 257.9534, 260.0286, 260.5632, 262.663, 264.723, 265.7566, 267.2566, 267.1624, 270.62, 272.8216, 273.2166, 275.2056, 276.2202, 278.3726, 280.3344, 281.9284, 283.9728, 284.1924, 286.4872, 287.587, 289.807, 291.1206, 292.769, 294.8708, 296.665, 297.1182, 299.4012, 300.6352, 302.1354, 304.1756, 306.1606, 307.3462, 308.5214, 309.4134, 310.8352, 313.9684, 315.837, 316.7796, 318.9858, },
+ // precision 7
+ (double[]) { 92, 93.4934, 94.9758, 96.4574, 97.9718, 99.4954, 101.5302, 103.0756, 104.6374, 106.1782, 107.7888, 109.9522, 111.592, 113.2532, 114.9086, 116.5938, 118.9474, 120.6796, 122.4394, 124.2176, 125.9768, 128.4214, 130.2528, 132.0102, 133.8658, 135.7278, 138.3044, 140.1316, 142.093, 144.0032, 145.9092, 148.6306, 150.5294, 152.5756, 154.6508, 156.662, 159.552, 161.3724, 163.617, 165.5754, 167.7872, 169.8444, 172.7988, 174.8606, 177.2118, 179.3566, 181.4476, 184.5882, 186.6816, 189.0824, 191.0258, 193.6048, 196.4436, 198.7274, 200.957, 203.147, 205.4364, 208.7592, 211.3386, 213.781, 215.8028, 218.656, 221.6544, 223.996, 226.4718, 229.1544, 231.6098, 234.5956, 237.0616, 239.5758, 242.4878, 244.5244, 248.2146, 250.724, 252.8722, 255.5198, 258.0414, 261.941, 264.9048, 266.87, 269.4304, 272.028, 274.4708, 278.37, 281.0624, 283.4668, 286.5532, 289.4352, 293.2564, 295.2744, 298.2118, 300.7472, 304.1456, 307.2928, 309.7504, 312.5528, 315.979, 318.2102, 322.1834, 324.3494, 327.325, 330.6614, 332.903, 337.2544, 339.9042, 343.215, 345.2864, 348.0814, 352.6764, 355.301, 357.139, 360.658, 363.1732, 366.5902, 369.9538, 373.0828, 375.922, 378.9902, 382.7328, 386.4538, 388.1136, 391.2234, 394.0878, 396.708, 401.1556, 404.1852, 406.6372, 409.6822, 412.7796, 416.6078, 418.4916, 422.131, 424.5376, 428.1988, 432.211, 434.4502, 438.5282, 440.912, 444.0448, 447.7432, 450.8524, 453.7988, 456.7858, 458.8868, 463.9886, 466.5064, 468.9124, 472.6616, 475.4682, 478.582, 481.304, 485.2738, 488.6894, 490.329, 496.106, 497.6908, 501.1374, 504.5322, 506.8848, 510.3324, 513.4512, 516.179, 520.4412, 522.6066, 526.167, 528.7794, 533.379, 536.067, 538.46, 542.9116, 545.692, 547.9546, 552.493, 555.2722, 557.335, 562.449, 564.2014, 569.0738, 571.0974, 574.8564, 578.2996, 581.409, 583.9704, 585.8098, 589.6528, 594.5998, 595.958, 600.068, 603.3278, 608.2016, 609.9632, 612.864, 615.43, 620.7794, 621.272, 625.8644, 629.206, 633.219, 634.5154, 638.6102, },
+ // precision 8
+ (double[]) { 184.2152, 187.2454, 190.2096, 193.6652, 196.6312, 199.6822, 203.249, 206.3296, 210.0038, 213.2074, 216.4612, 220.27, 223.5178, 227.4412, 230.8032, 234.1634, 238.1688, 241.6074, 245.6946, 249.2664, 252.8228, 257.0432, 260.6824, 264.9464, 268.6268, 272.2626, 276.8376, 280.4034, 284.8956, 288.8522, 292.7638, 297.3552, 301.3556, 305.7526, 309.9292, 313.8954, 318.8198, 322.7668, 327.298, 331.6688, 335.9466, 340.9746, 345.1672, 349.3474, 354.3028, 358.8912, 364.114, 368.4646, 372.9744, 378.4092, 382.6022, 387.843, 392.5684, 397.1652, 402.5426, 407.4152, 412.5388, 417.3592, 422.1366, 427.486, 432.3918, 437.5076, 442.509, 447.3834, 453.3498, 458.0668, 463.7346, 469.1228, 473.4528, 479.7, 484.644, 491.0518, 495.5774, 500.9068, 506.432, 512.1666, 517.434, 522.6644, 527.4894, 533.6312, 538.3804, 544.292, 550.5496, 556.0234, 562.8206, 566.6146, 572.4188, 579.117, 583.6762, 590.6576, 595.7864, 601.509, 607.5334, 612.9204, 619.772, 624.2924, 630.8654, 636.1836, 642.745, 649.1316, 655.0386, 660.0136, 666.6342, 671.6196, 678.1866, 684.4282, 689.3324, 695.4794, 702.5038, 708.129, 713.528, 720.3204, 726.463, 732.7928, 739.123, 744.7418, 751.2192, 756.5102, 762.6066, 769.0184, 775.2224, 781.4014, 787.7618, 794.1436, 798.6506, 805.6378, 811.766, 819.7514, 824.5776, 828.7322, 837.8048, 843.6302, 849.9336, 854.4798, 861.3388, 867.9894, 873.8196, 880.3136, 886.2308, 892.4588, 899.0816, 905.4076, 912.0064, 917.3878, 923.619, 929.998, 937.3482, 943.9506, 947.991, 955.1144, 962.203, 968.8222, 975.7324, 981.7826, 988.7666, 994.2648, 1000.3128, 1007.4082, 1013.7536, 1020.3376, 1026.7156, 1031.7478, 1037.4292, 1045.393, 1051.2278, 1058.3434, 1062.8726, 1071.884, 1076.806, 1082.9176, 1089.1678, 1095.5032, 1102.525, 1107.2264, 1115.315, 1120.93, 1127.252, 1134.1496, 1139.0408, 1147.5448, 1153.3296, 1158.1974, 1166.5262, 1174.3328, 1175.657, 1184.4222, 1190.9172, 1197.1292, 1204.4606, 1210.4578, 1218.8728, 1225.3336, 1226.6592, 1236.5768, 1241.363, 1249.4074, 1254.6566, 1260.8014, 1266.5454, 1274.5192, },
+ // precision 9
+ (double[]) { 369, 374.8294, 381.2452, 387.6698, 394.1464, 400.2024, 406.8782, 413.6598, 420.462, 427.2826, 433.7102, 440.7416, 447.9366, 455.1046, 462.285, 469.0668, 476.306, 483.8448, 491.301, 498.9886, 506.2422, 513.8138, 521.7074, 529.7428, 537.8402, 545.1664, 553.3534, 561.594, 569.6886, 577.7876, 585.65, 594.228, 602.8036, 611.1666, 620.0818, 628.0824, 637.2574, 646.302, 655.1644, 664.0056, 672.3802, 681.7192, 690.5234, 700.2084, 708.831, 718.485, 728.1112, 737.4764, 746.76, 756.3368, 766.5538, 775.5058, 785.2646, 795.5902, 804.3818, 814.8998, 824.9532, 835.2062, 845.2798, 854.4728, 864.9582, 875.3292, 886.171, 896.781, 906.5716, 916.7048, 927.5322, 937.875, 949.3972, 958.3464, 969.7274, 980.2834, 992.1444, 1003.4264, 1013.0166, 1024.018, 1035.0438, 1046.34, 1057.6856, 1068.9836, 1079.0312, 1091.677, 1102.3188, 1113.4846, 1124.4424, 1135.739, 1147.1488, 1158.9202, 1169.406, 1181.5342, 1193.2834, 1203.8954, 1216.3286, 1226.2146, 1239.6684, 1251.9946, 1262.123, 1275.4338, 1285.7378, 1296.076, 1308.9692, 1320.4964, 1333.0998, 1343.9864, 1357.7754, 1368.3208, 1380.4838, 1392.7388, 1406.0758, 1416.9098, 1428.9728, 1440.9228, 1453.9292, 1462.617, 1476.05, 1490.2996, 1500.6128, 1513.7392, 1524.5174, 1536.6322, 1548.2584, 1562.3766, 1572.423, 1587.1232, 1596.5164, 1610.5938, 1622.5972, 1633.1222, 1647.7674, 1658.5044, 1671.57, 1683.7044, 1695.4142, 1708.7102, 1720.6094, 1732.6522, 1747.841, 1756.4072, 1769.9786, 1782.3276, 1797.5216, 1808.3186, 1819.0694, 1834.354, 1844.575, 1856.2808, 1871.1288, 1880.7852, 1893.9622, 1906.3418, 1920.6548, 1932.9302, 1945.8584, 1955.473, 1968.8248, 1980.6446, 1995.9598, 2008.349, 2019.8556, 2033.0334, 2044.0206, 2059.3956, 2069.9174, 2082.6084, 2093.7036, 2106.6108, 2118.9124, 2132.301, 2144.7628, 2159.8422, 2171.0212, 2183.101, 2193.5112, 2208.052, 2221.3194, 2233.3282, 2247.295, 2257.7222, 2273.342, 2286.5638, 2299.6786, 2310.8114, 2322.3312, 2335.516, 2349.874, 2363.5968, 2373.865, 2387.1918, 2401.8328, 2414.8496, 2424.544, 2436.7592, 2447.1682, 2464.1958, 2474.3438, 2489.0006, 2497.4526, 2513.6586, 2527.19, 2540.7028, 2553.768 },
+ // precision 10
+ (double[]) { 738.1256, 750.4234, 763.1064, 775.4732, 788.4636, 801.0644, 814.488, 827.9654, 841.0832, 854.7864, 868.1992, 882.2176, 896.5228, 910.1716, 924.7752, 938.899, 953.6126, 968.6492, 982.9474, 998.5214, 1013.1064, 1028.6364, 1044.2468, 1059.4588, 1075.3832, 1091.0584, 1106.8606, 1123.3868, 1139.5062, 1156.1862, 1172.463, 1189.339, 1206.1936, 1223.1292, 1240.1854, 1257.2908, 1275.3324, 1292.8518, 1310.5204, 1328.4854, 1345.9318, 1364.552, 1381.4658, 1400.4256, 1419.849, 1438.152, 1456.8956, 1474.8792, 1494.118, 1513.62, 1532.5132, 1551.9322, 1570.7726, 1590.6086, 1610.5332, 1630.5918, 1650.4294, 1669.7662, 1690.4106, 1710.7338, 1730.9012, 1750.4486, 1770.1556, 1791.6338, 1812.7312, 1833.6264, 1853.9526, 1874.8742, 1896.8326, 1918.1966, 1939.5594, 1961.07, 1983.037, 2003.1804, 2026.071, 2047.4884, 2070.0848, 2091.2944, 2114.333, 2135.9626, 2158.2902, 2181.0814, 2202.0334, 2224.4832, 2246.39, 2269.7202, 2292.1714, 2314.2358, 2338.9346, 2360.891, 2384.0264, 2408.3834, 2430.1544, 2454.8684, 2476.9896, 2501.4368, 2522.8702, 2548.0408, 2570.6738, 2593.5208, 2617.0158, 2640.2302, 2664.0962, 2687.4986, 2714.2588, 2735.3914, 2759.6244, 2781.8378, 2808.0072, 2830.6516, 2856.2454, 2877.2136, 2903.4546, 2926.785, 2951.2294, 2976.468, 3000.867, 3023.6508, 3049.91, 3073.5984, 3098.162, 3121.5564, 3146.2328, 3170.9484, 3195.5902, 3221.3346, 3242.7032, 3271.6112, 3296.5546, 3317.7376, 3345.072, 3369.9518, 3394.326, 3418.1818, 3444.6926, 3469.086, 3494.2754, 3517.8698, 3544.248, 3565.3768, 3588.7234, 3616.979, 3643.7504, 3668.6812, 3695.72, 3719.7392, 3742.6224, 3770.4456, 3795.6602, 3819.9058, 3844.002, 3869.517, 3895.6824, 3920.8622, 3947.1364, 3973.985, 3995.4772, 4021.62, 4046.628, 4074.65, 4096.2256, 4121.831, 4146.6406, 4173.276, 4195.0744, 4223.9696, 4251.3708, 4272.9966, 4300.8046, 4326.302, 4353.1248, 4374.312, 4403.0322, 4426.819, 4450.0598, 4478.5206, 4504.8116, 4528.8928, 4553.9584, 4578.8712, 4603.8384, 4632.3872, 4655.5128, 4675.821, 4704.6222, 4731.9862, 4755.4174, 4781.2628, 4804.332, 4832.3048, 4862.8752, 4883.4148, 4906.9544, 4935.3516, 4954.3532, 4984.0248, 5011.217, 5035.3258, 5057.3672, 5084.1828, },
+ // precision 11
+ (double[]) { 1477, 1501.6014, 1526.5802, 1551.7942, 1577.3042, 1603.2062, 1629.8402, 1656.2292, 1682.9462, 1709.9926, 1737.3026, 1765.4252, 1793.0578, 1821.6092, 1849.626, 1878.5568, 1908.527, 1937.5154, 1967.1874, 1997.3878, 2027.37, 2058.1972, 2089.5728, 2120.1012, 2151.9668, 2183.292, 2216.0772, 2247.8578, 2280.6562, 2313.041, 2345.714, 2380.3112, 2414.1806, 2447.9854, 2481.656, 2516.346, 2551.5154, 2586.8378, 2621.7448, 2656.6722, 2693.5722, 2729.1462, 2765.4124, 2802.8728, 2838.898, 2876.408, 2913.4926, 2951.4938, 2989.6776, 3026.282, 3065.7704, 3104.1012, 3143.7388, 3181.6876, 3221.1872, 3261.5048, 3300.0214, 3339.806, 3381.409, 3421.4144, 3461.4294, 3502.2286, 3544.651, 3586.6156, 3627.337, 3670.083, 3711.1538, 3753.5094, 3797.01, 3838.6686, 3882.1678, 3922.8116, 3967.9978, 4009.9204, 4054.3286, 4097.5706, 4140.6014, 4185.544, 4229.5976, 4274.583, 4316.9438, 4361.672, 4406.2786, 4451.8628, 4496.1834, 4543.505, 4589.1816, 4632.5188, 4678.2294, 4724.8908, 4769.0194, 4817.052, 4861.4588, 4910.1596, 4956.4344, 5002.5238, 5048.13, 5093.6374, 5142.8162, 5187.7894, 5237.3984, 5285.6078, 5331.0858, 5379.1036, 5428.6258, 5474.6018, 5522.7618, 5571.5822, 5618.59, 5667.9992, 5714.88, 5763.454, 5808.6982, 5860.3644, 5910.2914, 5953.571, 6005.9232, 6055.1914, 6104.5882, 6154.5702, 6199.7036, 6251.1764, 6298.7596, 6350.0302, 6398.061, 6448.4694, 6495.933, 6548.0474, 6597.7166, 6646.9416, 6695.9208, 6742.6328, 6793.5276, 6842.1934, 6894.2372, 6945.3864, 6996.9228, 7044.2372, 7094.1374, 7142.2272, 7192.2942, 7238.8338, 7288.9006, 7344.0908, 7394.8544, 7443.5176, 7490.4148, 7542.9314, 7595.6738, 7641.9878, 7694.3688, 7743.0448, 7797.522, 7845.53, 7899.594, 7950.3132, 7996.455, 8050.9442, 8092.9114, 8153.1374, 8197.4472, 8252.8278, 8301.8728, 8348.6776, 8401.4698, 8453.551, 8504.6598, 8553.8944, 8604.1276, 8657.6514, 8710.3062, 8758.908, 8807.8706, 8862.1702, 8910.4668, 8960.77, 9007.2766, 9063.164, 9121.0534, 9164.1354, 9218.1594, 9267.767, 9319.0594, 9372.155, 9419.7126, 9474.3722, 9520.1338, 9572.368, 9622.7702, 9675.8448, 9726.5396, 9778.7378, 9827.6554, 9878.1922, 9928.7782, 9978.3984, 10026.578, 10076.5626, 10137.1618, 10177.5244, 10229.9176, },
+ // precision 12
+ (double[]) { 2954, 3003.4782, 3053.3568, 3104.3666, 3155.324, 3206.9598, 3259.648, 3312.539, 3366.1474, 3420.2576, 3474.8376, 3530.6076, 3586.451, 3643.38, 3700.4104, 3757.5638, 3815.9676, 3875.193, 3934.838, 3994.8548, 4055.018, 4117.1742, 4178.4482, 4241.1294, 4304.4776, 4367.4044, 4431.8724, 4496.3732, 4561.4304, 4627.5326, 4693.949, 4761.5532, 4828.7256, 4897.6182, 4965.5186, 5034.4528, 5104.865, 5174.7164, 5244.6828, 5316.6708, 5387.8312, 5459.9036, 5532.476, 5604.8652, 5679.6718, 5753.757, 5830.2072, 5905.2828, 5980.0434, 6056.6264, 6134.3192, 6211.5746, 6290.0816, 6367.1176, 6447.9796, 6526.5576, 6606.1858, 6686.9144, 6766.1142, 6847.0818, 6927.9664, 7010.9096, 7091.0816, 7175.3962, 7260.3454, 7344.018, 7426.4214, 7511.3106, 7596.0686, 7679.8094, 7765.818, 7852.4248, 7936.834, 8022.363, 8109.5066, 8200.4554, 8288.5832, 8373.366, 8463.4808, 8549.7682, 8642.0522, 8728.3288, 8820.9528, 8907.727, 9001.0794, 9091.2522, 9179.988, 9269.852, 9362.6394, 9453.642, 9546.9024, 9640.6616, 9732.6622, 9824.3254, 9917.7484, 10007.9392, 10106.7508, 10196.2152, 10289.8114, 10383.5494, 10482.3064, 10576.8734, 10668.7872, 10764.7156, 10862.0196, 10952.793, 11049.9748, 11146.0702, 11241.4492, 11339.2772, 11434.2336, 11530.741, 11627.6136, 11726.311, 11821.5964, 11918.837, 12015.3724, 12113.0162, 12213.0424, 12306.9804, 12408.4518, 12504.8968, 12604.586, 12700.9332, 12798.705, 12898.5142, 12997.0488, 13094.788, 13198.475, 13292.7764, 13392.9698, 13486.8574, 13590.1616, 13686.5838, 13783.6264, 13887.2638, 13992.0978, 14081.0844, 14189.9956, 14280.0912, 14382.4956, 14486.4384, 14588.1082, 14686.2392, 14782.276, 14888.0284, 14985.1864, 15088.8596, 15187.0998, 15285.027, 15383.6694, 15495.8266, 15591.3736, 15694.2008, 15790.3246, 15898.4116, 15997.4522, 16095.5014, 16198.8514, 16291.7492, 16402.6424, 16499.1266, 16606.2436, 16697.7186, 16796.3946, 16902.3376, 17005.7672, 17100.814, 17206.8282, 17305.8262, 17416.0744, 17508.4092, 17617.0178, 17715.4554, 17816.758, 17920.1748, 18012.9236, 18119.7984, 18223.2248, 18324.2482, 18426.6276, 18525.0932, 18629.8976, 18733.2588, 18831.0466, 18940.1366, 19032.2696, 19131.729, 19243.4864, 19349.6932, 19442.866, 19547.9448, 19653.2798, 19754.4034, 19854.0692, 19965.1224, 20065.1774, 20158.2212, 20253.353, 20366.3264, 20463.22, },
+ // precision 13
+ (double[]) { 5908.5052, 6007.2672, 6107.347, 6208.5794, 6311.2622, 6414.5514, 6519.3376, 6625.6952, 6732.5988, 6841.3552, 6950.5972, 7061.3082, 7173.5646, 7287.109, 7401.8216, 7516.4344, 7633.3802, 7751.2962, 7870.3784, 7990.292, 8110.79, 8233.4574, 8356.6036, 8482.2712, 8607.7708, 8735.099, 8863.1858, 8993.4746, 9123.8496, 9255.6794, 9388.5448, 9522.7516, 9657.3106, 9792.6094, 9930.5642, 10068.794, 10206.7256, 10347.81, 10490.3196, 10632.0778, 10775.9916, 10920.4662, 11066.124, 11213.073, 11358.0362, 11508.1006, 11659.1716, 11808.7514, 11959.4884, 12112.1314, 12265.037, 12420.3756, 12578.933, 12734.311, 12890.0006, 13047.2144, 13207.3096, 13368.5144, 13528.024, 13689.847, 13852.7528, 14018.3168, 14180.5372, 14346.9668, 14513.5074, 14677.867, 14846.2186, 15017.4186, 15184.9716, 15356.339, 15529.2972, 15697.3578, 15871.8686, 16042.187, 16216.4094, 16389.4188, 16565.9126, 16742.3272, 16919.0042, 17094.7592, 17273.965, 17451.8342, 17634.4254, 17810.5984, 17988.9242, 18171.051, 18354.7938, 18539.466, 18721.0408, 18904.9972, 19081.867, 19271.9118, 19451.8694, 19637.9816, 19821.2922, 20013.1292, 20199.3858, 20387.8726, 20572.9514, 20770.7764, 20955.1714, 21144.751, 21329.9952, 21520.709, 21712.7016, 21906.3868, 22096.2626, 22286.0524, 22475.051, 22665.5098, 22862.8492, 23055.5294, 23249.6138, 23437.848, 23636.273, 23826.093, 24020.3296, 24213.3896, 24411.7392, 24602.9614, 24805.7952, 24998.1552, 25193.9588, 25389.0166, 25585.8392, 25780.6976, 25981.2728, 26175.977, 26376.5252, 26570.1964, 26773.387, 26962.9812, 27163.0586, 27368.164, 27565.0534, 27758.7428, 27961.1276, 28163.2324, 28362.3816, 28565.7668, 28758.644, 28956.9768, 29163.4722, 29354.7026, 29561.1186, 29767.9948, 29959.9986, 30164.0492, 30366.9818, 30562.5338, 30762.9928, 30976.1592, 31166.274, 31376.722, 31570.3734, 31770.809, 31974.8934, 32179.5286, 32387.5442, 32582.3504, 32794.076, 32989.9528, 33191.842, 33392.4684, 33595.659, 33801.8672, 34000.3414, 34200.0922, 34402.6792, 34610.0638, 34804.0084, 35011.13, 35218.669, 35418.6634, 35619.0792, 35830.6534, 36028.4966, 36229.7902, 36438.6422, 36630.7764, 36833.3102, 37048.6728, 37247.3916, 37453.5904, 37669.3614, 37854.5526, 38059.305, 38268.0936, 38470.2516, 38674.7064, 38876.167, 39068.3794, 39281.9144, 39492.8566, 39684.8628, 39898.4108, 40093.1836, 40297.6858, 40489.7086, 40717.2424, },
+ // precision 14
+ (double[]) { 11817.475, 12015.0046, 12215.3792, 12417.7504, 12623.1814, 12830.0086, 13040.0072, 13252.503, 13466.178, 13683.2738, 13902.0344, 14123.9798, 14347.394, 14573.7784, 14802.6894, 15033.6824, 15266.9134, 15502.8624, 15741.4944, 15980.7956, 16223.8916, 16468.6316, 16715.733, 16965.5726, 17217.204, 17470.666, 17727.8516, 17986.7886, 18247.6902, 18510.9632, 18775.304, 19044.7486, 19314.4408, 19587.202, 19862.2576, 20135.924, 20417.0324, 20697.9788, 20979.6112, 21265.0274, 21550.723, 21841.6906, 22132.162, 22428.1406, 22722.127, 23020.5606, 23319.7394, 23620.4014, 23925.2728, 24226.9224, 24535.581, 24845.505, 25155.9618, 25470.3828, 25785.9702, 26103.7764, 26420.4132, 26742.0186, 27062.8852, 27388.415, 27714.6024, 28042.296, 28365.4494, 28701.1526, 29031.8008, 29364.2156, 29704.497, 30037.1458, 30380.111, 30723.8168, 31059.5114, 31404.9498, 31751.6752, 32095.2686, 32444.7792, 32794.767, 33145.204, 33498.4226, 33847.6502, 34209.006, 34560.849, 34919.4838, 35274.9778, 35635.1322, 35996.3266, 36359.1394, 36722.8266, 37082.8516, 37447.7354, 37815.9606, 38191.0692, 38559.4106, 38924.8112, 39294.6726, 39663.973, 40042.261, 40416.2036, 40779.2036, 41161.6436, 41540.9014, 41921.1998, 42294.7698, 42678.5264, 43061.3464, 43432.375, 43818.432, 44198.6598, 44583.0138, 44970.4794, 45353.924, 45729.858, 46118.2224, 46511.5724, 46900.7386, 47280.6964, 47668.1472, 48055.6796, 48446.9436, 48838.7146, 49217.7296, 49613.7796, 50010.7508, 50410.0208, 50793.7886, 51190.2456, 51583.1882, 51971.0796, 52376.5338, 52763.319, 53165.5534, 53556.5594, 53948.2702, 54346.352, 54748.7914, 55138.577, 55543.4824, 55941.1748, 56333.7746, 56745.1552, 57142.7944, 57545.2236, 57935.9956, 58348.5268, 58737.5474, 59158.5962, 59542.6896, 59958.8004, 60349.3788, 60755.0212, 61147.6144, 61548.194, 61946.0696, 62348.6042, 62763.603, 63162.781, 63560.635, 63974.3482, 64366.4908, 64771.5876, 65176.7346, 65597.3916, 65995.915, 66394.0384, 66822.9396, 67203.6336, 67612.2032, 68019.0078, 68420.0388, 68821.22, 69235.8388, 69640.0724, 70055.155, 70466.357, 70863.4266, 71276.2482, 71677.0306, 72080.2006, 72493.0214, 72893.5952, 73314.5856, 73714.9852, 74125.3022, 74521.2122, 74933.6814, 75341.5904, 75743.0244, 76166.0278, 76572.1322, 76973.1028, 77381.6284, 77800.6092, 78189.328, 78607.0962, 79012.2508, 79407.8358, 79825.725, 80238.701, 80646.891, 81035.6436, 81460.0448, 81876.3884, },
+ // precision 15
+ (double[]) { 23635.0036, 24030.8034, 24431.4744, 24837.1524, 25246.7928, 25661.326, 26081.3532, 26505.2806, 26933.9892, 27367.7098, 27805.318, 28248.799, 28696.4382, 29148.8244, 29605.5138, 30066.8668, 30534.2344, 31006.32, 31480.778, 31962.2418, 32447.3324, 32938.0232, 33432.731, 33930.728, 34433.9896, 34944.1402, 35457.5588, 35974.5958, 36497.3296, 37021.9096, 37554.326, 38088.0826, 38628.8816, 39171.3192, 39723.2326, 40274.5554, 40832.3142, 41390.613, 41959.5908, 42532.5466, 43102.0344, 43683.5072, 44266.694, 44851.2822, 45440.7862, 46038.0586, 46640.3164, 47241.064, 47846.155, 48454.7396, 49076.9168, 49692.542, 50317.4778, 50939.65, 51572.5596, 52210.2906, 52843.7396, 53481.3996, 54127.236, 54770.406, 55422.6598, 56078.7958, 56736.7174, 57397.6784, 58064.5784, 58730.308, 59404.9784, 60077.0864, 60751.9158, 61444.1386, 62115.817, 62808.7742, 63501.4774, 64187.5454, 64883.6622, 65582.7468, 66274.5318, 66976.9276, 67688.7764, 68402.138, 69109.6274, 69822.9706, 70543.6108, 71265.5202, 71983.3848, 72708.4656, 73433.384, 74158.4664, 74896.4868, 75620.9564, 76362.1434, 77098.3204, 77835.7662, 78582.6114, 79323.9902, 80067.8658, 80814.9246, 81567.0136, 82310.8536, 83061.9952, 83821.4096, 84580.8608, 85335.547, 86092.5802, 86851.6506, 87612.311, 88381.2016, 89146.3296, 89907.8974, 90676.846, 91451.4152, 92224.5518, 92995.8686, 93763.5066, 94551.2796, 95315.1944, 96096.1806, 96881.0918, 97665.679, 98442.68, 99229.3002, 100011.0994, 100790.6386, 101580.1564, 102377.7484, 103152.1392, 103944.2712, 104730.216, 105528.6336, 106324.9398, 107117.6706, 107890.3988, 108695.2266, 109485.238, 110294.7876, 111075.0958, 111878.0496, 112695.2864, 113464.5486, 114270.0474, 115068.608, 115884.3626, 116673.2588, 117483.3716, 118275.097, 119085.4092, 119879.2808, 120687.5868, 121499.9944, 122284.916, 123095.9254, 123912.5038, 124709.0454, 125503.7182, 126323.259, 127138.9412, 127943.8294, 128755.646, 129556.5354, 130375.3298, 131161.4734, 131971.1962, 132787.5458, 133588.1056, 134431.351, 135220.2906, 136023.398, 136846.6558, 137667.0004, 138463.663, 139283.7154, 140074.6146, 140901.3072, 141721.8548, 142543.2322, 143356.1096, 144173.7412, 144973.0948, 145794.3162, 146609.5714, 147420.003, 148237.9784, 149050.5696, 149854.761, 150663.1966, 151494.0754, 152313.1416, 153112.6902, 153935.7206, 154746.9262, 155559.547, 156401.9746, 157228.7036, 158008.7254, 158820.75, 159646.9184, 160470.4458, 161279.5348, 162093.3114, 162918.542, 163729.2842, },
+ // precision 16
+ (double[]) { 47271, 48062.3584, 48862.7074, 49673.152, 50492.8416, 51322.9514, 52161.03, 53009.407, 53867.6348, 54734.206, 55610.5144, 56496.2096, 57390.795, 58297.268, 59210.6448, 60134.665, 61068.0248, 62010.4472, 62962.5204, 63923.5742, 64895.0194, 65876.4182, 66862.6136, 67862.6968, 68868.8908, 69882.8544, 70911.271, 71944.0924, 72990.0326, 74040.692, 75100.6336, 76174.7826, 77252.5998, 78340.2974, 79438.2572, 80545.4976, 81657.2796, 82784.6336, 83915.515, 85059.7362, 86205.9368, 87364.4424, 88530.3358, 89707.3744, 90885.9638, 92080.197, 93275.5738, 94479.391, 95695.918, 96919.2236, 98148.4602, 99382.3474, 100625.6974, 101878.0284, 103141.6278, 104409.4588, 105686.2882, 106967.5402, 108261.6032, 109548.1578, 110852.0728, 112162.231, 113479.0072, 114806.2626, 116137.9072, 117469.5048, 118813.5186, 120165.4876, 121516.2556, 122875.766, 124250.5444, 125621.2222, 127003.2352, 128387.848, 129775.2644, 131181.7776, 132577.3086, 133979.9458, 135394.1132, 136800.9078, 138233.217, 139668.5308, 141085.212, 142535.2122, 143969.0684, 145420.2872, 146878.1542, 148332.7572, 149800.3202, 151269.66, 152743.6104, 154213.0948, 155690.288, 157169.4246, 158672.1756, 160160.059, 161650.6854, 163145.7772, 164645.6726, 166159.1952, 167682.1578, 169177.3328, 170700.0118, 172228.8964, 173732.6664, 175265.5556, 176787.799, 178317.111, 179856.6914, 181400.865, 182943.4612, 184486.742, 186033.4698, 187583.7886, 189148.1868, 190688.4526, 192250.1926, 193810.9042, 195354.2972, 196938.7682, 198493.5898, 200079.2824, 201618.912, 203205.5492, 204765.5798, 206356.1124, 207929.3064, 209498.7196, 211086.229, 212675.1324, 214256.7892, 215826.2392, 217412.8474, 218995.6724, 220618.6038, 222207.1166, 223781.0364, 225387.4332, 227005.7928, 228590.4336, 230217.8738, 231805.1054, 233408.9, 234995.3432, 236601.4956, 238190.7904, 239817.2548, 241411.2832, 243002.4066, 244640.1884, 246255.3128, 247849.3508, 249479.9734, 251106.8822, 252705.027, 254332.9242, 255935.129, 257526.9014, 259154.772, 260777.625, 262390.253, 264004.4906, 265643.59, 267255.4076, 268873.426, 270470.7252, 272106.4804, 273722.4456, 275337.794, 276945.7038, 278592.9154, 280204.3726, 281841.1606, 283489.171, 285130.1716, 286735.3362, 288364.7164, 289961.1814, 291595.5524, 293285.683, 294899.6668, 296499.3434, 298128.0462, 299761.8946, 301394.2424, 302997.6748, 304615.1478, 306269.7724, 307886.114, 309543.1028, 311153.2862, 312782.8546, 314421.2008, 316033.2438, 317692.9636, 319305.2648, 320948.7406, 322566.3364, 324228.4224, 325847.1542, },
+ // precision 17
+ (double[]) { 94542, 96125.811, 97728.019, 99348.558, 100987.9705, 102646.7565, 104324.5125, 106021.7435, 107736.7865, 109469.272, 111223.9465, 112995.219, 114787.432, 116593.152, 118422.71, 120267.2345, 122134.6765, 124020.937, 125927.2705, 127851.255, 129788.9485, 131751.016, 133726.8225, 135722.592, 137736.789, 139770.568, 141821.518, 143891.343, 145982.1415, 148095.387, 150207.526, 152355.649, 154515.6415, 156696.05, 158887.7575, 161098.159, 163329.852, 165569.053, 167837.4005, 170121.6165, 172420.4595, 174732.6265, 177062.77, 179412.502, 181774.035, 184151.939, 186551.6895, 188965.691, 191402.8095, 193857.949, 196305.0775, 198774.6715, 201271.2585, 203764.78, 206299.3695, 208818.1365, 211373.115, 213946.7465, 216532.076, 219105.541, 221714.5375, 224337.5135, 226977.5125, 229613.0655, 232270.2685, 234952.2065, 237645.3555, 240331.1925, 243034.517, 245756.0725, 248517.6865, 251232.737, 254011.3955, 256785.995, 259556.44, 262368.335, 265156.911, 267965.266, 270785.583, 273616.0495, 276487.4835, 279346.639, 282202.509, 285074.3885, 287942.2855, 290856.018, 293774.0345, 296678.5145, 299603.6355, 302552.6575, 305492.9785, 308466.8605, 311392.581, 314347.538, 317319.4295, 320285.9785, 323301.7325, 326298.3235, 329301.3105, 332301.987, 335309.791, 338370.762, 341382.923, 344431.1265, 347464.1545, 350507.28, 353619.2345, 356631.2005, 359685.203, 362776.7845, 365886.488, 368958.2255, 372060.6825, 375165.4335, 378237.935, 381328.311, 384430.5225, 387576.425, 390683.242, 393839.648, 396977.8425, 400101.9805, 403271.296, 406409.8425, 409529.5485, 412678.7, 415847.423, 419020.8035, 422157.081, 425337.749, 428479.6165, 431700.902, 434893.1915, 438049.582, 441210.5415, 444379.2545, 447577.356, 450741.931, 453959.548, 457137.0935, 460329.846, 463537.4815, 466732.3345, 469960.5615, 473164.681, 476347.6345, 479496.173, 482813.1645, 486025.6995, 489249.4885, 492460.1945, 495675.8805, 498908.0075, 502131.802, 505374.3855, 508550.9915, 511806.7305, 515026.776, 518217.0005, 521523.9855, 524705.9855, 527950.997, 531210.0265, 534472.497, 537750.7315, 540926.922, 544207.094, 547429.4345, 550666.3745, 553975.3475, 557150.7185, 560399.6165, 563662.697, 566916.7395, 570146.1215, 573447.425, 576689.6245, 579874.5745, 583202.337, 586503.0255, 589715.635, 592910.161, 596214.3885, 599488.035, 602740.92, 605983.0685, 609248.67, 612491.3605, 615787.912, 619107.5245, 622307.9555, 625577.333, 628840.4385, 632085.2155, 635317.6135, 638691.7195, 641887.467, 645139.9405, 648441.546, 651666.252, 654941.845, },
+ // precision 18
+ (double[]) { 189084, 192250.913, 195456.774, 198696.946, 201977.762, 205294.444, 208651.754, 212042.099, 215472.269, 218941.91, 222443.912, 225996.845, 229568.199, 233193.568, 236844.457, 240543.233, 244279.475, 248044.27, 251854.588, 255693.2, 259583.619, 263494.621, 267445.385, 271454.061, 275468.769, 279549.456, 283646.446, 287788.198, 291966.099, 296181.164, 300431.469, 304718.618, 309024.004, 313393.508, 317760.803, 322209.731, 326675.061, 331160.627, 335654.47, 340241.442, 344841.833, 349467.132, 354130.629, 358819.432, 363574.626, 368296.587, 373118.482, 377914.93, 382782.301, 387680.669, 392601.981, 397544.323, 402529.115, 407546.018, 412593.658, 417638.657, 422762.865, 427886.169, 433017.167, 438213.273, 443441.254, 448692.421, 453937.533, 459239.049, 464529.569, 469910.083, 475274.03, 480684.473, 486070.26, 491515.237, 496995.651, 502476.617, 507973.609, 513497.19, 519083.233, 524726.509, 530305.505, 535945.728, 541584.404, 547274.055, 552967.236, 558667.862, 564360.216, 570128.148, 575965.08, 581701.952, 587532.523, 593361.144, 599246.128, 605033.418, 610958.779, 616837.117, 622772.818, 628672.04, 634675.369, 640574.831, 646585.739, 652574.547, 658611.217, 664642.684, 670713.914, 676737.681, 682797.313, 688837.897, 694917.874, 701009.882, 707173.648, 713257.254, 719415.392, 725636.761, 731710.697, 737906.209, 744103.074, 750313.39, 756504.185, 762712.579, 768876.985, 775167.859, 781359, 787615.959, 793863.597, 800245.477, 806464.582, 812785.294, 819005.925, 825403.057, 831676.197, 837936.284, 844266.968, 850642.711, 856959.756, 863322.774, 869699.931, 876102.478, 882355.787, 888694.463, 895159.952, 901536.143, 907872.631, 914293.672, 920615.14, 927130.974, 933409.404, 939922.178, 946331.47, 952745.93, 959209.264, 965590.224, 972077.284, 978501.961, 984953.19, 991413.271, 997817.479, 1004222.658, 1010725.676, 1017177.138, 1023612.529, 1030098.236, 1036493.719, 1043112.207, 1049537.036, 1056008.096, 1062476.184, 1068942.337, 1075524.95, 1081932.864, 1088426.025, 1094776.005, 1101327.448, 1107901.673, 1114423.639, 1120884.602, 1127324.923, 1133794.24, 1140328.886, 1146849.376, 1153346.682, 1159836.502, 1166478.703, 1172953.304, 1179391.502, 1185950.982, 1192544.052, 1198913.41, 1205430.994, 1212015.525, 1218674.042, 1225121.683, 1231551.101, 1238126.379, 1244673.795, 1251260.649, 1257697.86, 1264320.983, 1270736.319, 1277274.694, 1283804.95, 1290211.514, 1296858.568, 1303455.691, }
+};
+
+static const double *g_biasData[] = {
+ // precision 4
+ (double[]) { 10, 9.717, 9.207, 8.7896, 8.2882, 7.8204, 7.3772, 6.9342, 6.5202, 6.161, 5.7722, 5.4636, 5.0396, 4.6766, 4.3566, 4.0454, 3.7936, 3.4856, 3.2666, 2.9946, 2.766, 2.4692, 2.3638, 2.0764, 1.7864, 1.7602, 1.4814, 1.433, 1.2926, 1.0664, 0.999600000000001, 0.7956, 0.5366, 0.589399999999998, 0.573799999999999, 0.269799999999996, 0.368200000000002, 0.0544000000000011, 0.234200000000001, 0.0108000000000033, -0.203400000000002, -0.0701999999999998, -0.129600000000003, -0.364199999999997, -0.480600000000003, -0.226999999999997, -0.322800000000001, -0.382599999999996, -0.511200000000002, -0.669600000000003, -0.749400000000001, -0.500399999999999, -0.617600000000003, -0.6922, -0.601599999999998, -0.416200000000003, -0.338200000000001, -0.782600000000002, -0.648600000000002, -0.919800000000002, -0.851799999999997, -0.962400000000002, -0.6402, -1.1922, -1.0256, -1.086, -1.21899999999999, -0.819400000000002, -0.940600000000003, -1.1554, -1.2072, -1.1752, -1.16759999999999, -1.14019999999999, -1.3754, -1.29859999999999, -1.607, -1.3292, -1.7606, },
+ // precision 5
+ (double[]) { 22, 21.1194, 20.8208, 20.2318, 19.77, 19.2436, 18.7774, 18.2848, 17.8224, 17.3742, 16.9336, 16.503, 16.0494, 15.6292, 15.2124, 14.798, 14.367, 13.9728, 13.5944, 13.217, 12.8438, 12.3696, 12.0956, 11.7044, 11.324, 11.0668, 10.6698, 10.3644, 10.049, 9.6918, 9.4146, 9.082, 8.687, 8.5398, 8.2462, 7.857, 7.6606, 7.4168, 7.1248, 6.9222, 6.6804, 6.447, 6.3454, 5.9594, 5.7636, 5.5776, 5.331, 5.19, 4.9676, 4.7564, 4.5314, 4.4442, 4.3708, 3.9774, 3.9624, 3.8796, 3.755, 3.472, 3.2076, 3.1024, 2.8908, 2.7338, 2.7728, 2.629, 2.413, 2.3266, 2.1524, 2.2642, 2.1806, 2.0566, 1.9192, 1.7598, 1.3516, 1.5802, 1.43859999999999, 1.49160000000001, 1.1524, 1.1892, 0.841399999999993, 0.879800000000003, 0.837599999999995, 0.469800000000006, 0.765600000000006, 0.331000000000003, 0.591399999999993, 0.601200000000006, 0.701599999999999, 0.558199999999999, 0.339399999999998, 0.354399999999998, 0.491200000000006, 0.308000000000007, 0.355199999999996, -0.0254000000000048, 0.205200000000005, -0.272999999999996, 0.132199999999997, 0.394400000000005, -0.241200000000006, 0.242000000000004, 0.191400000000002, 0.253799999999998, -0.122399999999999, -0.370800000000003, 0.193200000000004, -0.0848000000000013, 0.0867999999999967, -0.327200000000005, -0.285600000000002, 0.311400000000006, -0.128399999999999, -0.754999999999995, -0.209199999999996, -0.293599999999998, -0.364000000000004, -0.253600000000006, -0.821200000000005, -0.253600000000006, -0.510400000000004, -0.383399999999995, -0.491799999999998, -0.220200000000006, -0.0972000000000008, -0.557400000000001, -0.114599999999996, -0.295000000000002, -0.534800000000004, 0.346399999999988, -0.65379999999999, 0.0398000000000138, 0.0341999999999985, -0.995800000000003, -0.523400000000009, -0.489000000000004, -0.274799999999999, -0.574999999999989, -0.482799999999997, 0.0571999999999946, -0.330600000000004, -0.628800000000012, -0.140199999999993, -0.540600000000012, -0.445999999999998, -0.599400000000003, -0.262599999999992, 0.163399999999996, -0.100599999999986, -0.39500000000001, -1.06960000000001, -0.836399999999998, -0.753199999999993, -0.412399999999991, -0.790400000000005, -0.29679999999999, -0.28540000000001, -0.193000000000012, -0.0772000000000048, -0.962799999999987, -0.414800000000014, },
+ // precision 6
+ (double[]) { 45, 44.1902, 43.271, 42.8358, 41.8142, 41.2854, 40.317, 39.354, 38.8924, 37.9436, 37.4596, 36.5262, 35.6248, 35.1574, 34.2822, 33.837, 32.9636, 32.074, 31.7042, 30.7976, 30.4772, 29.6564, 28.7942, 28.5004, 27.686, 27.291, 26.5672, 25.8556, 25.4982, 24.8204, 24.4252, 23.7744, 23.0786, 22.8344, 22.0294, 21.8098, 21.0794, 20.5732, 20.1878, 19.5648, 19.2902, 18.6784, 18.3352, 17.8946, 17.3712, 17.0852, 16.499, 16.2686, 15.6844, 15.2234, 14.9732, 14.3356, 14.2286, 13.7262, 13.3284, 13.1048, 12.5962, 12.3562, 12.1272, 11.4184, 11.4974, 11.0822, 10.856, 10.48, 10.2834, 10.0208, 9.637, 9.51739999999999, 9.05759999999999, 8.74760000000001, 8.42700000000001, 8.1326, 8.2372, 8.2788, 7.6776, 7.79259999999999, 7.1952, 6.9564, 6.6454, 6.87, 6.5428, 6.19999999999999, 6.02940000000001, 5.62780000000001, 5.6782, 5.792, 5.35159999999999, 5.28319999999999, 5.0394, 5.07480000000001, 4.49119999999999, 4.84899999999999, 4.696, 4.54040000000001, 4.07300000000001, 4.37139999999999, 3.7216, 3.7328, 3.42080000000001, 3.41839999999999, 3.94239999999999, 3.27719999999999, 3.411, 3.13079999999999, 2.76900000000001, 2.92580000000001, 2.68279999999999, 2.75020000000001, 2.70599999999999, 2.3886, 3.01859999999999, 2.45179999999999, 2.92699999999999, 2.41720000000001, 2.41139999999999, 2.03299999999999, 2.51240000000001, 2.5564, 2.60079999999999, 2.41720000000001, 1.80439999999999, 1.99700000000001, 2.45480000000001, 1.8948, 2.2346, 2.30860000000001, 2.15479999999999, 1.88419999999999, 1.6508, 0.677199999999999, 1.72540000000001, 1.4752, 1.72280000000001, 1.66139999999999, 1.16759999999999, 1.79300000000001, 1.00059999999999, 0.905200000000008, 0.659999999999997, 1.55879999999999, 1.1636, 0.688199999999995, 0.712600000000009, 0.450199999999995, 1.1978, 0.975599999999986, 0.165400000000005, 1.727, 1.19739999999999, -0.252600000000001, 1.13460000000001, 1.3048, 1.19479999999999, 0.313400000000001, 0.878999999999991, 1.12039999999999, 0.853000000000009, 1.67920000000001, 0.856999999999999, 0.448599999999999, 1.2362, 0.953399999999988, 1.02859999999998, 0.563199999999995, 0.663000000000011, 0.723000000000013, 0.756599999999992, 0.256599999999992, -0.837600000000009, 0.620000000000005, 0.821599999999989, 0.216600000000028, 0.205600000000004, 0.220199999999977, 0.372599999999977, 0.334400000000016, 0.928400000000011, 0.972800000000007, 0.192400000000021, 0.487199999999973, -0.413000000000011, 0.807000000000016, 0.120600000000024, 0.769000000000005, 0.870799999999974, 0.66500000000002, 0.118200000000002, 0.401200000000017, 0.635199999999998, 0.135400000000004, 0.175599999999974, 1.16059999999999, 0.34620000000001, 0.521400000000028, -0.586599999999976, -1.16480000000001, 0.968399999999974, 0.836999999999989, 0.779600000000016, 0.985799999999983, },
+ // precision 7
+ (double[]) { 91, 89.4934, 87.9758, 86.4574, 84.9718, 83.4954, 81.5302, 80.0756, 78.6374, 77.1782, 75.7888, 73.9522, 72.592, 71.2532, 69.9086, 68.5938, 66.9474, 65.6796, 64.4394, 63.2176, 61.9768, 60.4214, 59.2528, 58.0102, 56.8658, 55.7278, 54.3044, 53.1316, 52.093, 51.0032, 49.9092, 48.6306, 47.5294, 46.5756, 45.6508, 44.662, 43.552, 42.3724, 41.617, 40.5754, 39.7872, 38.8444, 37.7988, 36.8606, 36.2118, 35.3566, 34.4476, 33.5882, 32.6816, 32.0824, 31.0258, 30.6048, 29.4436, 28.7274, 27.957, 27.147, 26.4364, 25.7592, 25.3386, 24.781, 23.8028, 23.656, 22.6544, 21.996, 21.4718, 21.1544, 20.6098, 19.5956, 19.0616, 18.5758, 18.4878, 17.5244, 17.2146, 16.724, 15.8722, 15.5198, 15.0414, 14.941, 14.9048, 13.87, 13.4304, 13.028, 12.4708, 12.37, 12.0624, 11.4668, 11.5532, 11.4352, 11.2564, 10.2744, 10.2118, 9.74720000000002, 10.1456, 9.2928, 8.75040000000001, 8.55279999999999, 8.97899999999998, 8.21019999999999, 8.18340000000001, 7.3494, 7.32499999999999, 7.66140000000001, 6.90300000000002, 7.25439999999998, 6.9042, 7.21499999999997, 6.28640000000001, 6.08139999999997, 6.6764, 6.30099999999999, 5.13900000000001, 5.65800000000002, 5.17320000000001, 4.59019999999998, 4.9538, 5.08280000000002, 4.92200000000003, 4.99020000000002, 4.7328, 5.4538, 4.11360000000002, 4.22340000000003, 4.08780000000002, 3.70800000000003, 4.15559999999999, 4.18520000000001, 3.63720000000001, 3.68220000000002, 3.77960000000002, 3.6078, 2.49160000000001, 3.13099999999997, 2.5376, 3.19880000000001, 3.21100000000001, 2.4502, 3.52820000000003, 2.91199999999998, 3.04480000000001, 2.7432, 2.85239999999999, 2.79880000000003, 2.78579999999999, 1.88679999999999, 2.98860000000002, 2.50639999999999, 1.91239999999999, 2.66160000000002, 2.46820000000002, 1.58199999999999, 1.30399999999997, 2.27379999999999, 2.68939999999998, 1.32900000000001, 3.10599999999999, 1.69080000000002, 2.13740000000001, 2.53219999999999, 1.88479999999998, 1.33240000000001, 1.45119999999997, 1.17899999999997, 2.44119999999998, 1.60659999999996, 2.16700000000003, 0.77940000000001, 2.37900000000002, 2.06700000000001, 1.46000000000004, 2.91160000000002, 1.69200000000001, 0.954600000000028, 2.49300000000005, 2.2722, 1.33500000000004, 2.44899999999996, 1.20140000000004, 3.07380000000001, 2.09739999999999, 2.85640000000001, 2.29960000000005, 2.40899999999999, 1.97040000000004, 0.809799999999996, 1.65279999999996, 2.59979999999996, 0.95799999999997, 2.06799999999998, 2.32780000000002, 4.20159999999998, 1.96320000000003, 1.86400000000003, 1.42999999999995, 3.77940000000001, 1.27200000000005, 1.86440000000005, 2.20600000000002, 3.21900000000005, 1.5154, 2.61019999999996, },
+ // precision 8
+ (double[]) { 183.2152, 180.2454, 177.2096, 173.6652, 170.6312, 167.6822, 164.249, 161.3296, 158.0038, 155.2074, 152.4612, 149.27, 146.5178, 143.4412, 140.8032, 138.1634, 135.1688, 132.6074, 129.6946, 127.2664, 124.8228, 122.0432, 119.6824, 116.9464, 114.6268, 112.2626, 109.8376, 107.4034, 104.8956, 102.8522, 100.7638, 98.3552, 96.3556, 93.7526, 91.9292, 89.8954, 87.8198, 85.7668, 83.298, 81.6688, 79.9466, 77.9746, 76.1672, 74.3474, 72.3028, 70.8912, 69.114, 67.4646, 65.9744, 64.4092, 62.6022, 60.843, 59.5684, 58.1652, 56.5426, 55.4152, 53.5388, 52.3592, 51.1366, 49.486, 48.3918, 46.5076, 45.509, 44.3834, 43.3498, 42.0668, 40.7346, 40.1228, 38.4528, 37.7, 36.644, 36.0518, 34.5774, 33.9068, 32.432, 32.1666, 30.434, 29.6644, 28.4894, 27.6312, 26.3804, 26.292, 25.5496000000001, 25.0234, 24.8206, 22.6146, 22.4188, 22.117, 20.6762, 20.6576, 19.7864, 19.509, 18.5334, 17.9204, 17.772, 16.2924, 16.8654, 15.1836, 15.745, 15.1316, 15.0386, 14.0136, 13.6342, 12.6196, 12.1866, 12.4281999999999, 11.3324, 10.4794000000001, 11.5038, 10.129, 9.52800000000002, 10.3203999999999, 9.46299999999997, 9.79280000000006, 9.12300000000005, 8.74180000000001, 9.2192, 7.51020000000005, 7.60659999999996, 7.01840000000004, 7.22239999999999, 7.40139999999997, 6.76179999999999, 7.14359999999999, 5.65060000000005, 5.63779999999997, 5.76599999999996, 6.75139999999999, 5.57759999999996, 3.73220000000003, 5.8048, 5.63019999999995, 4.93359999999996, 3.47979999999995, 4.33879999999999, 3.98940000000005, 3.81960000000004, 3.31359999999995, 3.23080000000004, 3.4588, 3.08159999999998, 3.4076, 3.00639999999999, 2.38779999999997, 2.61900000000003, 1.99800000000005, 3.34820000000002, 2.95060000000001, 0.990999999999985, 2.11440000000005, 2.20299999999997, 2.82219999999995, 2.73239999999998, 2.7826, 3.76660000000004, 2.26480000000004, 2.31280000000004, 2.40819999999997, 2.75360000000001, 3.33759999999995, 2.71559999999999, 1.7478000000001, 1.42920000000004, 2.39300000000003, 2.22779999999989, 2.34339999999997, 0.87259999999992, 3.88400000000001, 1.80600000000004, 1.91759999999999, 1.16779999999994, 1.50320000000011, 2.52500000000009, 0.226400000000012, 2.31500000000005, 0.930000000000064, 1.25199999999995, 2.14959999999996, 0.0407999999999902, 2.5447999999999, 1.32960000000003, 0.197400000000016, 2.52620000000002, 3.33279999999991, -1.34300000000007, 0.422199999999975, 0.917200000000093, 1.12920000000008, 1.46060000000011, 1.45779999999991, 2.8728000000001, 3.33359999999993, -1.34079999999994, 1.57680000000005, 0.363000000000056, 1.40740000000005, 0.656600000000026, 0.801400000000058, -0.454600000000028, 1.51919999999996, },
+ // precision 9
+ (double[]) { 368, 361.8294, 355.2452, 348.6698, 342.1464, 336.2024, 329.8782, 323.6598, 317.462, 311.2826, 305.7102, 299.7416, 293.9366, 288.1046, 282.285, 277.0668, 271.306, 265.8448, 260.301, 254.9886, 250.2422, 244.8138, 239.7074, 234.7428, 229.8402, 225.1664, 220.3534, 215.594, 210.6886, 205.7876, 201.65, 197.228, 192.8036, 188.1666, 184.0818, 180.0824, 176.2574, 172.302, 168.1644, 164.0056, 160.3802, 156.7192, 152.5234, 149.2084, 145.831, 142.485, 139.1112, 135.4764, 131.76, 129.3368, 126.5538, 122.5058, 119.2646, 116.5902, 113.3818, 110.8998, 107.9532, 105.2062, 102.2798, 99.4728, 96.9582, 94.3292, 92.171, 89.7809999999999, 87.5716, 84.7048, 82.5322, 79.875, 78.3972, 75.3464, 73.7274, 71.2834, 70.1444, 68.4263999999999, 66.0166, 64.018, 62.0437999999999, 60.3399999999999, 58.6856, 57.9836, 55.0311999999999, 54.6769999999999, 52.3188, 51.4846, 49.4423999999999, 47.739, 46.1487999999999, 44.9202, 43.4059999999999, 42.5342000000001, 41.2834, 38.8954000000001, 38.3286000000001, 36.2146, 36.6684, 35.9946, 33.123, 33.4338, 31.7378000000001, 29.076, 28.9692, 27.4964, 27.0998, 25.9864, 26.7754, 24.3208, 23.4838, 22.7388000000001, 24.0758000000001, 21.9097999999999, 20.9728, 19.9228000000001, 19.9292, 16.617, 17.05, 18.2996000000001, 15.6128000000001, 15.7392, 14.5174, 13.6322, 12.2583999999999, 13.3766000000001, 11.423, 13.1232, 9.51639999999998, 10.5938000000001, 9.59719999999993, 8.12220000000002, 9.76739999999995, 7.50440000000003, 7.56999999999994, 6.70440000000008, 6.41419999999994, 6.71019999999999, 5.60940000000005, 4.65219999999999, 6.84099999999989, 3.4072000000001, 3.97859999999991, 3.32760000000007, 5.52160000000003, 3.31860000000006, 2.06940000000009, 4.35400000000004, 1.57500000000005, 0.280799999999999, 2.12879999999996, -0.214799999999968, -0.0378000000000611, -0.658200000000079, 0.654800000000023, -0.0697999999999865, 0.858400000000074, -2.52700000000004, -2.1751999999999, -3.35539999999992, -1.04019999999991, -0.651000000000067, -2.14439999999991, -1.96659999999997, -3.97939999999994, -0.604400000000169, -3.08260000000018, -3.39159999999993, -5.29640000000018, -5.38920000000007, -5.08759999999984, -4.69900000000007, -5.23720000000003, -3.15779999999995, -4.97879999999986, -4.89899999999989, -7.48880000000008, -5.94799999999987, -5.68060000000014, -6.67180000000008, -4.70499999999993, -7.27779999999984, -4.6579999999999, -4.4362000000001, -4.32139999999981, -5.18859999999995, -6.66879999999992, -6.48399999999992, -5.1260000000002, -4.4032000000002, -6.13500000000022, -5.80819999999994, -4.16719999999987, -4.15039999999999, -7.45600000000013, -7.24080000000004, -9.83179999999993, -5.80420000000004, -8.6561999999999, -6.99940000000015, -10.5473999999999, -7.34139999999979, -6.80999999999995, -6.29719999999998, -6.23199999999997, },
+ // precision 10
+ (double[]) { 737.1256, 724.4234, 711.1064, 698.4732, 685.4636, 673.0644, 660.488, 647.9654, 636.0832, 623.7864, 612.1992, 600.2176, 588.5228, 577.1716, 565.7752, 554.899, 543.6126, 532.6492, 521.9474, 511.5214, 501.1064, 490.6364, 480.2468, 470.4588, 460.3832, 451.0584, 440.8606, 431.3868, 422.5062, 413.1862, 404.463, 395.339, 386.1936, 378.1292, 369.1854, 361.2908, 353.3324, 344.8518, 337.5204, 329.4854, 321.9318, 314.552, 306.4658, 299.4256, 292.849, 286.152, 278.8956, 271.8792, 265.118, 258.62, 252.5132, 245.9322, 239.7726, 233.6086, 227.5332, 222.5918, 216.4294, 210.7662, 205.4106, 199.7338, 194.9012, 188.4486, 183.1556, 178.6338, 173.7312, 169.6264, 163.9526, 159.8742, 155.8326, 151.1966, 147.5594, 143.07, 140.037, 134.1804, 131.071, 127.4884, 124.0848, 120.2944, 117.333, 112.9626, 110.2902, 107.0814, 103.0334, 99.4832000000001, 96.3899999999999, 93.7202000000002, 90.1714000000002, 87.2357999999999, 85.9346, 82.8910000000001, 80.0264000000002, 78.3834000000002, 75.1543999999999, 73.8683999999998, 70.9895999999999, 69.4367999999999, 64.8701999999998, 65.0408000000002, 61.6738, 59.5207999999998, 57.0158000000001, 54.2302, 53.0962, 50.4985999999999, 52.2588000000001, 47.3914, 45.6244000000002, 42.8377999999998, 43.0072, 40.6516000000001, 40.2453999999998, 35.2136, 36.4546, 33.7849999999999, 33.2294000000002, 32.4679999999998, 30.8670000000002, 28.6507999999999, 28.9099999999999, 27.5983999999999, 26.1619999999998, 24.5563999999999, 23.2328000000002, 21.9484000000002, 21.5902000000001, 21.3346000000001, 17.7031999999999, 20.6111999999998, 19.5545999999999, 15.7375999999999, 17.0720000000001, 16.9517999999998, 15.326, 13.1817999999998, 14.6925999999999, 13.0859999999998, 13.2754, 10.8697999999999, 11.248, 7.3768, 4.72339999999986, 7.97899999999981, 8.7503999999999, 7.68119999999999, 9.7199999999998, 7.73919999999998, 5.6224000000002, 7.44560000000001, 6.6601999999998, 5.9058, 4.00199999999995, 4.51699999999983, 4.68240000000014, 3.86220000000003, 5.13639999999987, 5.98500000000013, 2.47719999999981, 2.61999999999989, 1.62800000000016, 4.65000000000009, 0.225599999999758, 0.831000000000131, -0.359400000000278, 1.27599999999984, -2.92559999999958, -0.0303999999996449, 2.37079999999969, -2.0033999999996, 0.804600000000391, 0.30199999999968, 1.1247999999996, -2.6880000000001, 0.0321999999996478, -1.18099999999959, -3.9402, -1.47940000000017, -0.188400000000001, -2.10720000000038, -2.04159999999956, -3.12880000000041, -4.16160000000036, -0.612799999999879, -3.48719999999958, -8.17900000000009, -5.37780000000021, -4.01379999999972, -5.58259999999973, -5.73719999999958, -7.66799999999967, -5.69520000000011, -1.1247999999996, -5.58520000000044, -8.04560000000038, -4.64840000000004, -11.6468000000004, -7.97519999999986, -5.78300000000036, -7.67420000000038, -10.6328000000003, -9.81720000000041, },
+ // precision 11
+ (double[]) { 1476, 1449.6014, 1423.5802, 1397.7942, 1372.3042, 1347.2062, 1321.8402, 1297.2292, 1272.9462, 1248.9926, 1225.3026, 1201.4252, 1178.0578, 1155.6092, 1132.626, 1110.5568, 1088.527, 1066.5154, 1045.1874, 1024.3878, 1003.37, 982.1972, 962.5728, 942.1012, 922.9668, 903.292, 884.0772, 864.8578, 846.6562, 828.041, 809.714, 792.3112, 775.1806, 757.9854, 740.656, 724.346, 707.5154, 691.8378, 675.7448, 659.6722, 645.5722, 630.1462, 614.4124, 600.8728, 585.898, 572.408, 558.4926, 544.4938, 531.6776, 517.282, 505.7704, 493.1012, 480.7388, 467.6876, 456.1872, 445.5048, 433.0214, 420.806, 411.409, 400.4144, 389.4294, 379.2286, 369.651, 360.6156, 350.337, 342.083, 332.1538, 322.5094, 315.01, 305.6686, 298.1678, 287.8116, 280.9978, 271.9204, 265.3286, 257.5706, 249.6014, 242.544, 235.5976, 229.583, 220.9438, 214.672, 208.2786, 201.8628, 195.1834, 191.505, 186.1816, 178.5188, 172.2294, 167.8908, 161.0194, 158.052, 151.4588, 148.1596, 143.4344, 138.5238, 133.13, 127.6374, 124.8162, 118.7894, 117.3984, 114.6078, 109.0858, 105.1036, 103.6258, 98.6018000000004, 95.7618000000002, 93.5821999999998, 88.5900000000001, 86.9992000000002, 82.8800000000001, 80.4539999999997, 74.6981999999998, 74.3644000000004, 73.2914000000001, 65.5709999999999, 66.9232000000002, 65.1913999999997, 62.5882000000001, 61.5702000000001, 55.7035999999998, 56.1764000000003, 52.7596000000003, 53.0302000000001, 49.0609999999997, 48.4694, 44.933, 46.0474000000004, 44.7165999999997, 41.9416000000001, 39.9207999999999, 35.6328000000003, 35.5276000000003, 33.1934000000001, 33.2371999999996, 33.3864000000003, 33.9228000000003, 30.2371999999996, 29.1373999999996, 25.2272000000003, 24.2942000000003, 19.8338000000003, 18.9005999999999, 23.0907999999999, 21.8544000000002, 19.5176000000001, 15.4147999999996, 16.9314000000004, 18.6737999999996, 12.9877999999999, 14.3688000000002, 12.0447999999997, 15.5219999999999, 12.5299999999997, 14.5940000000001, 14.3131999999996, 9.45499999999993, 12.9441999999999, 3.91139999999996, 13.1373999999996, 5.44720000000052, 9.82779999999912, 7.87279999999919, 3.67760000000089, 5.46980000000076, 5.55099999999948, 5.65979999999945, 3.89439999999922, 3.1275999999998, 5.65140000000065, 6.3062000000009, 3.90799999999945, 1.87060000000019, 5.17020000000048, 2.46680000000015, 0.770000000000437, -3.72340000000077, 1.16400000000067, 8.05340000000069, 0.135399999999208, 2.15940000000046, 0.766999999999825, 1.0594000000001, 3.15500000000065, -0.287399999999252, 2.37219999999979, -2.86620000000039, -1.63199999999961, -2.22979999999916, -0.15519999999924, -1.46039999999994, -0.262199999999211, -2.34460000000036, -2.8078000000005, -3.22179999999935, -5.60159999999996, -8.42200000000048, -9.43740000000071, 0.161799999999857, -10.4755999999998, -10.0823999999993, },
+ // precision 12
+ (double[]) { 2953, 2900.4782, 2848.3568, 2796.3666, 2745.324, 2694.9598, 2644.648, 2595.539, 2546.1474, 2498.2576, 2450.8376, 2403.6076, 2357.451, 2311.38, 2266.4104, 2221.5638, 2176.9676, 2134.193, 2090.838, 2048.8548, 2007.018, 1966.1742, 1925.4482, 1885.1294, 1846.4776, 1807.4044, 1768.8724, 1731.3732, 1693.4304, 1657.5326, 1621.949, 1586.5532, 1551.7256, 1517.6182, 1483.5186, 1450.4528, 1417.865, 1385.7164, 1352.6828, 1322.6708, 1291.8312, 1260.9036, 1231.476, 1201.8652, 1173.6718, 1145.757, 1119.2072, 1092.2828, 1065.0434, 1038.6264, 1014.3192, 988.5746, 965.0816, 940.1176, 917.9796, 894.5576, 871.1858, 849.9144, 827.1142, 805.0818, 783.9664, 763.9096, 742.0816, 724.3962, 706.3454, 688.018, 667.4214, 650.3106, 633.0686, 613.8094, 597.818, 581.4248, 563.834, 547.363, 531.5066, 520.455400000001, 505.583199999999, 488.366, 476.480799999999, 459.7682, 450.0522, 434.328799999999, 423.952799999999, 408.727000000001, 399.079400000001, 387.252200000001, 373.987999999999, 360.852000000001, 351.6394, 339.642, 330.902400000001, 322.661599999999, 311.662200000001, 301.3254, 291.7484, 279.939200000001, 276.7508, 263.215200000001, 254.811400000001, 245.5494, 242.306399999999, 234.8734, 223.787200000001, 217.7156, 212.0196, 200.793, 195.9748, 189.0702, 182.449199999999, 177.2772, 170.2336, 164.741, 158.613600000001, 155.311, 147.5964, 142.837, 137.3724, 132.0162, 130.0424, 121.9804, 120.451800000001, 114.8968, 111.585999999999, 105.933199999999, 101.705, 98.5141999999996, 95.0488000000005, 89.7880000000005, 91.4750000000004, 83.7764000000006, 80.9698000000008, 72.8574000000008, 73.1615999999995, 67.5838000000003, 62.6263999999992, 63.2638000000006, 66.0977999999996, 52.0843999999997, 58.9956000000002, 47.0912000000008, 46.4956000000002, 48.4383999999991, 47.1082000000006, 43.2392, 37.2759999999998, 40.0283999999992, 35.1864000000005, 35.8595999999998, 32.0998, 28.027, 23.6694000000007, 33.8266000000003, 26.3736000000008, 27.2008000000005, 21.3245999999999, 26.4115999999995, 23.4521999999997, 19.5013999999992, 19.8513999999996, 10.7492000000002, 18.6424000000006, 13.1265999999996, 18.2436000000016, 6.71860000000015, 3.39459999999963, 6.33759999999893, 7.76719999999841, 0.813999999998487, 3.82819999999992, 0.826199999999517, 8.07440000000133, -1.59080000000176, 5.01780000000144, 0.455399999998917, -0.24199999999837, 0.174800000000687, -9.07640000000174, -4.20160000000033, -3.77520000000004, -4.75179999999818, -5.3724000000002, -8.90680000000066, -6.10239999999976, -5.74120000000039, -9.95339999999851, -3.86339999999836, -13.7304000000004, -16.2710000000006, -7.51359999999841, -3.30679999999847, -13.1339999999982, -10.0551999999989, -6.72019999999975, -8.59660000000076, -10.9307999999983, -1.8775999999998, -4.82259999999951, -13.7788, -21.6470000000008, -10.6735999999983, -15.7799999999988, },
+ // precision 13
+ (double[]) { 5907.5052, 5802.2672, 5697.347, 5593.5794, 5491.2622, 5390.5514, 5290.3376, 5191.6952, 5093.5988, 4997.3552, 4902.5972, 4808.3082, 4715.5646, 4624.109, 4533.8216, 4444.4344, 4356.3802, 4269.2962, 4183.3784, 4098.292, 4014.79, 3932.4574, 3850.6036, 3771.2712, 3691.7708, 3615.099, 3538.1858, 3463.4746, 3388.8496, 3315.6794, 3244.5448, 3173.7516, 3103.3106, 3033.6094, 2966.5642, 2900.794, 2833.7256, 2769.81, 2707.3196, 2644.0778, 2583.9916, 2523.4662, 2464.124, 2406.073, 2347.0362, 2292.1006, 2238.1716, 2182.7514, 2128.4884, 2077.1314, 2025.037, 1975.3756, 1928.933, 1879.311, 1831.0006, 1783.2144, 1738.3096, 1694.5144, 1649.024, 1606.847, 1564.7528, 1525.3168, 1482.5372, 1443.9668, 1406.5074, 1365.867, 1329.2186, 1295.4186, 1257.9716, 1225.339, 1193.2972, 1156.3578, 1125.8686, 1091.187, 1061.4094, 1029.4188, 1000.9126, 972.3272, 944.004199999999, 915.7592, 889.965, 862.834200000001, 840.4254, 812.598399999999, 785.924200000001, 763.050999999999, 741.793799999999, 721.466, 699.040799999999, 677.997200000002, 649.866999999998, 634.911800000002, 609.8694, 591.981599999999, 570.2922, 557.129199999999, 538.3858, 521.872599999999, 502.951400000002, 495.776399999999, 475.171399999999, 459.751, 439.995200000001, 426.708999999999, 413.7016, 402.3868, 387.262599999998, 372.0524, 357.050999999999, 342.5098, 334.849200000001, 322.529399999999, 311.613799999999, 295.848000000002, 289.273000000001, 274.093000000001, 263.329600000001, 251.389599999999, 245.7392, 231.9614, 229.7952, 217.155200000001, 208.9588, 199.016599999999, 190.839199999999, 180.6976, 176.272799999999, 166.976999999999, 162.5252, 151.196400000001, 149.386999999999, 133.981199999998, 130.0586, 130.164000000001, 122.053400000001, 110.7428, 108.1276, 106.232400000001, 100.381600000001, 98.7668000000012, 86.6440000000002, 79.9768000000004, 82.4722000000002, 68.7026000000005, 70.1186000000016, 71.9948000000004, 58.998599999999, 59.0492000000013, 56.9818000000014, 47.5338000000011, 42.9928, 51.1591999999982, 37.2740000000013, 42.7220000000016, 31.3734000000004, 26.8090000000011, 25.8934000000008, 26.5286000000015, 29.5442000000003, 19.3503999999994, 26.0760000000009, 17.9527999999991, 14.8419999999969, 10.4683999999979, 8.65899999999965, 9.86720000000059, 4.34139999999752, -0.907800000000861, -3.32080000000133, -0.936199999996461, -11.9916000000012, -8.87000000000262, -6.33099999999831, -11.3366000000024, -15.9207999999999, -9.34659999999712, -15.5034000000014, -19.2097999999969, -15.357799999998, -28.2235999999975, -30.6898000000001, -19.3271999999997, -25.6083999999973, -24.409599999999, -13.6385999999984, -33.4473999999973, -32.6949999999997, -28.9063999999998, -31.7483999999968, -32.2935999999972, -35.8329999999987, -47.620600000002, -39.0855999999985, -33.1434000000008, -46.1371999999974, -37.5892000000022, -46.8164000000033, -47.3142000000007, -60.2914000000019, -37.7575999999972, },
+ // precision 14
+ (double[]) { 11816.475, 11605.0046, 11395.3792, 11188.7504, 10984.1814, 10782.0086, 10582.0072, 10384.503, 10189.178, 9996.2738, 9806.0344, 9617.9798, 9431.394, 9248.7784, 9067.6894, 8889.6824, 8712.9134, 8538.8624, 8368.4944, 8197.7956, 8031.8916, 7866.6316, 7703.733, 7544.5726, 7386.204, 7230.666, 7077.8516, 6926.7886, 6778.6902, 6631.9632, 6487.304, 6346.7486, 6206.4408, 6070.202, 5935.2576, 5799.924, 5671.0324, 5541.9788, 5414.6112, 5290.0274, 5166.723, 5047.6906, 4929.162, 4815.1406, 4699.127, 4588.5606, 4477.7394, 4369.4014, 4264.2728, 4155.9224, 4055.581, 3955.505, 3856.9618, 3761.3828, 3666.9702, 3575.7764, 3482.4132, 3395.0186, 3305.8852, 3221.415, 3138.6024, 3056.296, 2970.4494, 2896.1526, 2816.8008, 2740.2156, 2670.497, 2594.1458, 2527.111, 2460.8168, 2387.5114, 2322.9498, 2260.6752, 2194.2686, 2133.7792, 2074.767, 2015.204, 1959.4226, 1898.6502, 1850.006, 1792.849, 1741.4838, 1687.9778, 1638.1322, 1589.3266, 1543.1394, 1496.8266, 1447.8516, 1402.7354, 1361.9606, 1327.0692, 1285.4106, 1241.8112, 1201.6726, 1161.973, 1130.261, 1094.2036, 1048.2036, 1020.6436, 990.901400000002, 961.199800000002, 924.769800000002, 899.526400000002, 872.346400000002, 834.375, 810.432000000001, 780.659800000001, 756.013800000001, 733.479399999997, 707.923999999999, 673.858, 652.222399999999, 636.572399999997, 615.738599999997, 586.696400000001, 564.147199999999, 541.679600000003, 523.943599999999, 505.714599999999, 475.729599999999, 461.779600000002, 449.750800000002, 439.020799999998, 412.7886, 400.245600000002, 383.188199999997, 362.079599999997, 357.533799999997, 334.319000000003, 327.553399999997, 308.559399999998, 291.270199999999, 279.351999999999, 271.791400000002, 252.576999999997, 247.482400000001, 236.174800000001, 218.774599999997, 220.155200000001, 208.794399999999, 201.223599999998, 182.995600000002, 185.5268, 164.547400000003, 176.5962, 150.689599999998, 157.8004, 138.378799999999, 134.021200000003, 117.614399999999, 108.194000000003, 97.0696000000025, 89.6042000000016, 95.6030000000028, 84.7810000000027, 72.635000000002, 77.3482000000004, 59.4907999999996, 55.5875999999989, 50.7346000000034, 61.3916000000027, 50.9149999999936, 39.0384000000049, 58.9395999999979, 29.633600000001, 28.2032000000036, 26.0078000000067, 17.0387999999948, 9.22000000000116, 13.8387999999977, 8.07240000000456, 14.1549999999988, 15.3570000000036, 3.42660000000615, 6.24820000000182, -2.96940000000177, -8.79940000000352, -5.97860000000219, -14.4048000000039, -3.4143999999942, -13.0148000000045, -11.6977999999945, -25.7878000000055, -22.3185999999987, -24.409599999999, -31.9756000000052, -18.9722000000038, -22.8678000000073, -30.8972000000067, -32.3715999999986, -22.3907999999938, -43.6720000000059, -35.9038, -39.7492000000057, -54.1641999999993, -45.2749999999942, -42.2989999999991, -44.1089999999967, -64.3564000000042, -49.9551999999967, -42.6116000000038, },
+ // precision 15
+ (double[]) { 23634.0036, 23210.8034, 22792.4744, 22379.1524, 21969.7928, 21565.326, 21165.3532, 20770.2806, 20379.9892, 19994.7098, 19613.318, 19236.799, 18865.4382, 18498.8244, 18136.5138, 17778.8668, 17426.2344, 17079.32, 16734.778, 16397.2418, 16063.3324, 15734.0232, 15409.731, 15088.728, 14772.9896, 14464.1402, 14157.5588, 13855.5958, 13559.3296, 13264.9096, 12978.326, 12692.0826, 12413.8816, 12137.3192, 11870.2326, 11602.5554, 11340.3142, 11079.613, 10829.5908, 10583.5466, 10334.0344, 10095.5072, 9859.694, 9625.2822, 9395.7862, 9174.0586, 8957.3164, 8738.064, 8524.155, 8313.7396, 8116.9168, 7913.542, 7718.4778, 7521.65, 7335.5596, 7154.2906, 6968.7396, 6786.3996, 6613.236, 6437.406, 6270.6598, 6107.7958, 5945.7174, 5787.6784, 5635.5784, 5482.308, 5337.9784, 5190.0864, 5045.9158, 4919.1386, 4771.817, 4645.7742, 4518.4774, 4385.5454, 4262.6622, 4142.74679999999, 4015.5318, 3897.9276, 3790.7764, 3685.13800000001, 3573.6274, 3467.9706, 3368.61079999999, 3271.5202, 3170.3848, 3076.4656, 2982.38400000001, 2888.4664, 2806.4868, 2711.9564, 2634.1434, 2551.3204, 2469.7662, 2396.61139999999, 2318.9902, 2243.8658, 2171.9246, 2105.01360000001, 2028.8536, 1960.9952, 1901.4096, 1841.86079999999, 1777.54700000001, 1714.5802, 1654.65059999999, 1596.311, 1546.2016, 1492.3296, 1433.8974, 1383.84600000001, 1339.4152, 1293.5518, 1245.8686, 1193.50659999999, 1162.27959999999, 1107.19439999999, 1069.18060000001, 1035.09179999999, 999.679000000004, 957.679999999993, 925.300199999998, 888.099400000006, 848.638600000006, 818.156400000007, 796.748399999997, 752.139200000005, 725.271200000003, 692.216, 671.633600000001, 647.939799999993, 621.670599999998, 575.398799999995, 561.226599999995, 532.237999999998, 521.787599999996, 483.095799999996, 467.049599999998, 465.286399999997, 415.548599999995, 401.047399999996, 380.607999999993, 377.362599999993, 347.258799999996, 338.371599999999, 310.096999999994, 301.409199999995, 276.280799999993, 265.586800000005, 258.994399999996, 223.915999999997, 215.925399999993, 213.503800000006, 191.045400000003, 166.718200000003, 166.259000000005, 162.941200000001, 148.829400000002, 141.645999999993, 123.535399999993, 122.329800000007, 89.473399999988, 80.1962000000058, 77.5457999999926, 59.1056000000099, 83.3509999999951, 52.2906000000075, 36.3979999999865, 40.6558000000077, 42.0003999999899, 19.6630000000005, 19.7153999999864, -8.38539999999921, -0.692799999989802, 0.854800000000978, 3.23219999999856, -3.89040000000386, -5.25880000001052, -24.9052000000083, -22.6837999999989, -26.4286000000138, -34.997000000003, -37.0216000000073, -43.430400000012, -58.2390000000014, -68.8034000000043, -56.9245999999985, -57.8583999999973, -77.3097999999882, -73.2793999999994, -81.0738000000129, -87.4530000000086, -65.0254000000132, -57.296399999992, -96.2746000000043, -103.25, -96.081600000005, -91.5542000000132, -102.465200000006, -107.688599999994, -101.458000000013, -109.715800000005, },
+ // precision 16
+ (double[]) { 47270, 46423.3584, 45585.7074, 44757.152, 43938.8416, 43130.9514, 42330.03, 41540.407, 40759.6348, 39988.206, 39226.5144, 38473.2096, 37729.795, 36997.268, 36272.6448, 35558.665, 34853.0248, 34157.4472, 33470.5204, 32793.5742, 32127.0194, 31469.4182, 30817.6136, 30178.6968, 29546.8908, 28922.8544, 28312.271, 27707.0924, 27114.0326, 26526.692, 25948.6336, 25383.7826, 24823.5998, 24272.2974, 23732.2572, 23201.4976, 22674.2796, 22163.6336, 21656.515, 21161.7362, 20669.9368, 20189.4424, 19717.3358, 19256.3744, 18795.9638, 18352.197, 17908.5738, 17474.391, 17052.918, 16637.2236, 16228.4602, 15823.3474, 15428.6974, 15043.0284, 14667.6278, 14297.4588, 13935.2882, 13578.5402, 13234.6032, 12882.1578, 12548.0728, 12219.231, 11898.0072, 11587.2626, 11279.9072, 10973.5048, 10678.5186, 10392.4876, 10105.2556, 9825.766, 9562.5444, 9294.2222, 9038.2352, 8784.848, 8533.2644, 8301.7776, 8058.30859999999, 7822.94579999999, 7599.11319999999, 7366.90779999999, 7161.217, 6957.53080000001, 6736.212, 6548.21220000001, 6343.06839999999, 6156.28719999999, 5975.15419999999, 5791.75719999999, 5621.32019999999, 5451.66, 5287.61040000001, 5118.09479999999, 4957.288, 4798.4246, 4662.17559999999, 4512.05900000001, 4364.68539999999, 4220.77720000001, 4082.67259999999, 3957.19519999999, 3842.15779999999, 3699.3328, 3583.01180000001, 3473.8964, 3338.66639999999, 3233.55559999999, 3117.799, 3008.111, 2909.69140000001, 2814.86499999999, 2719.46119999999, 2624.742, 2532.46979999999, 2444.7886, 2370.1868, 2272.45259999999, 2196.19260000001, 2117.90419999999, 2023.2972, 1969.76819999999, 1885.58979999999, 1833.2824, 1733.91200000001, 1682.54920000001, 1604.57980000001, 1556.11240000001, 1491.3064, 1421.71960000001, 1371.22899999999, 1322.1324, 1264.7892, 1196.23920000001, 1143.8474, 1088.67240000001, 1073.60380000001, 1023.11660000001, 959.036400000012, 927.433199999999, 906.792799999996, 853.433599999989, 841.873800000001, 791.1054, 756.899999999994, 704.343200000003, 672.495599999995, 622.790399999998, 611.254799999995, 567.283200000005, 519.406599999988, 519.188400000014, 495.312800000014, 451.350799999986, 443.973399999988, 431.882199999993, 392.027000000002, 380.924200000009, 345.128999999986, 298.901400000002, 287.771999999997, 272.625, 247.253000000026, 222.490600000019, 223.590000000026, 196.407599999977, 176.425999999978, 134.725199999986, 132.4804, 110.445599999977, 86.7939999999944, 56.7038000000175, 64.915399999998, 38.3726000000024, 37.1606000000029, 46.170999999973, 49.1716000000015, 15.3362000000197, 6.71639999997569, -34.8185999999987, -39.4476000000141, 12.6830000000191, -12.3331999999937, -50.6565999999875, -59.9538000000175, -65.1054000000004, -70.7576000000117, -106.325200000021, -126.852200000023, -110.227599999984, -132.885999999999, -113.897200000007, -142.713800000027, -151.145399999979, -150.799200000009, -177.756200000003, -156.036399999983, -182.735199999996, -177.259399999981, -198.663600000029, -174.577600000019, -193.84580000001, },
+ // precision 17
+ (double[]) { 94541, 92848.811, 91174.019, 89517.558, 87879.9705, 86262.7565, 84663.5125, 83083.7435, 81521.7865, 79977.272, 78455.9465, 76950.219, 75465.432, 73994.152, 72546.71, 71115.2345, 69705.6765, 68314.937, 66944.2705, 65591.255, 64252.9485, 62938.016, 61636.8225, 60355.592, 59092.789, 57850.568, 56624.518, 55417.343, 54231.1415, 53067.387, 51903.526, 50774.649, 49657.6415, 48561.05, 47475.7575, 46410.159, 45364.852, 44327.053, 43318.4005, 42325.6165, 41348.4595, 40383.6265, 39436.77, 38509.502, 37594.035, 36695.939, 35818.6895, 34955.691, 34115.8095, 33293.949, 32465.0775, 31657.6715, 30877.2585, 30093.78, 29351.3695, 28594.1365, 27872.115, 27168.7465, 26477.076, 25774.541, 25106.5375, 24452.5135, 23815.5125, 23174.0655, 22555.2685, 21960.2065, 21376.3555, 20785.1925, 20211.517, 19657.0725, 19141.6865, 18579.737, 18081.3955, 17578.995, 17073.44, 16608.335, 16119.911, 15651.266, 15194.583, 14749.0495, 14343.4835, 13925.639, 13504.509, 13099.3885, 12691.2855, 12328.018, 11969.0345, 11596.5145, 11245.6355, 10917.6575, 10580.9785, 10277.8605, 9926.58100000001, 9605.538, 9300.42950000003, 8989.97850000003, 8728.73249999998, 8448.3235, 8175.31050000002, 7898.98700000002, 7629.79100000003, 7413.76199999999, 7149.92300000001, 6921.12650000001, 6677.1545, 6443.28000000003, 6278.23450000002, 6014.20049999998, 5791.20299999998, 5605.78450000001, 5438.48800000001, 5234.2255, 5059.6825, 4887.43349999998, 4682.935, 4496.31099999999, 4322.52250000002, 4191.42499999999, 4021.24200000003, 3900.64799999999, 3762.84250000003, 3609.98050000001, 3502.29599999997, 3363.84250000003, 3206.54849999998, 3079.70000000001, 2971.42300000001, 2867.80349999998, 2727.08100000001, 2630.74900000001, 2496.6165, 2440.902, 2356.19150000002, 2235.58199999999, 2120.54149999999, 2012.25449999998, 1933.35600000003, 1820.93099999998, 1761.54800000001, 1663.09350000002, 1578.84600000002, 1509.48149999999, 1427.3345, 1379.56150000001, 1306.68099999998, 1212.63449999999, 1084.17300000001, 1124.16450000001, 1060.69949999999, 1007.48849999998, 941.194499999983, 879.880500000028, 836.007500000007, 782.802000000025, 748.385499999975, 647.991500000004, 626.730500000005, 570.776000000013, 484.000500000024, 513.98550000001, 418.985499999952, 386.996999999974, 370.026500000036, 355.496999999974, 356.731499999994, 255.92200000002, 259.094000000041, 205.434499999974, 165.374500000034, 197.347500000033, 95.718499999959, 67.6165000000037, 54.6970000000438, 31.7395000000251, -15.8784999999916, 8.42500000004657, -26.3754999999655, -118.425500000012, -66.6629999999423, -42.9745000000112, -107.364999999991, -189.839000000036, -162.611499999999, -164.964999999967, -189.079999999958, -223.931499999948, -235.329999999958, -269.639500000048, -249.087999999989, -206.475499999942, -283.04449999996, -290.667000000016, -304.561499999953, -336.784499999951, -380.386500000022, -283.280499999993, -364.533000000054, -389.059499999974, -364.454000000027, -415.748000000021, -417.155000000028, },
+ // precision 18
+ (double[]) { 189083, 185696.913, 182348.774, 179035.946, 175762.762, 172526.444, 169329.754, 166166.099, 163043.269, 159958.91, 156907.912, 153906.845, 150924.199, 147996.568, 145093.457, 142239.233, 139421.475, 136632.27, 133889.588, 131174.2, 128511.619, 125868.621, 123265.385, 120721.061, 118181.769, 115709.456, 113252.446, 110840.198, 108465.099, 106126.164, 103823.469, 101556.618, 99308.004, 97124.508, 94937.803, 92833.731, 90745.061, 88677.627, 86617.47, 84650.442, 82697.833, 80769.132, 78879.629, 77014.432, 75215.626, 73384.587, 71652.482, 69895.93, 68209.301, 66553.669, 64921.981, 63310.323, 61742.115, 60205.018, 58698.658, 57190.657, 55760.865, 54331.169, 52908.167, 51550.273, 50225.254, 48922.421, 47614.533, 46362.049, 45098.569, 43926.083, 42736.03, 41593.473, 40425.26, 39316.237, 38243.651, 37170.617, 36114.609, 35084.19, 34117.233, 33206.509, 32231.505, 31318.728, 30403.404, 29540.0550000001, 28679.236, 27825.862, 26965.216, 26179.148, 25462.08, 24645.952, 23922.523, 23198.144, 22529.128, 21762.4179999999, 21134.779, 20459.117, 19840.818, 19187.04, 18636.3689999999, 17982.831, 17439.7389999999, 16874.547, 16358.2169999999, 15835.684, 15352.914, 14823.681, 14329.313, 13816.897, 13342.874, 12880.882, 12491.648, 12021.254, 11625.392, 11293.7610000001, 10813.697, 10456.209, 10099.074, 9755.39000000001, 9393.18500000006, 9047.57900000003, 8657.98499999999, 8395.85900000005, 8033, 7736.95900000003, 7430.59699999995, 7258.47699999996, 6924.58200000005, 6691.29399999999, 6357.92500000005, 6202.05700000003, 5921.19700000004, 5628.28399999999, 5404.96799999999, 5226.71100000001, 4990.75600000005, 4799.77399999998, 4622.93099999998, 4472.478, 4171.78700000001, 3957.46299999999, 3868.95200000005, 3691.14300000004, 3474.63100000005, 3341.67200000002, 3109.14000000001, 3071.97400000005, 2796.40399999998, 2756.17799999996, 2611.46999999997, 2471.93000000005, 2382.26399999997, 2209.22400000005, 2142.28399999999, 2013.96100000001, 1911.18999999994, 1818.27099999995, 1668.47900000005, 1519.65800000005, 1469.67599999998, 1367.13800000004, 1248.52899999998, 1181.23600000003, 1022.71900000004, 1088.20700000005, 959.03600000008, 876.095999999903, 791.183999999892, 703.337000000058, 731.949999999953, 586.86400000006, 526.024999999907, 323.004999999888, 320.448000000091, 340.672999999952, 309.638999999966, 216.601999999955, 102.922999999952, 19.2399999999907, -0.114000000059605, -32.6240000000689, -89.3179999999702, -153.497999999905, -64.2970000000205, -143.695999999996, -259.497999999905, -253.017999999924, -213.948000000091, -397.590000000084, -434.006000000052, -403.475000000093, -297.958000000101, -404.317000000039, -528.898999999976, -506.621000000043, -513.205000000075, -479.351000000024, -596.139999999898, -527.016999999993, -664.681000000099, -680.306000000099, -704.050000000047, -850.486000000034, -757.43200000003, -713.308999999892, }
+};
+/*
+ * Returns the bias correctors from the
+ * hyperloglog paper
+ */
+static double alpha(unsigned char precision) {
+ switch (precision) {
+ case 4:
+ return 0.673;
+ case 5:
+ return 0.697;
+ case 6:
+ return 0.709;
+ default:
+ return 0.7213 / (1 + 1.079 / NUM_REG(precision));
+ }
+}
+
+// Estimates cardinality using a linear counting. Used when some registers still have a zero value.
+static double linear_count(int num_reg, int num_zero)
+{
+ double est = num_reg * log((double)num_reg / (double)num_zero);
+ return est;
+}
+/**
+ * Binary searches for the nearest matching index
+ * @return The matching index, or closest match
+ */
+static int binary_search(double val, int num, const double *array) {
+ int low=0, mid, high=num-1;
+ while (low < high) {
+ mid = (low + high) / 2;
+ if (val > array[mid]) {
+ low = mid + 1;
+ } else if (val == array[mid]) {
+ return mid;
+ } else {
+ high = mid - 1;
+ }
+ }
+ return low;
+}
+/**
+ * Interpolates the bias estimate using the
+ * empircal data collected by Google, from the
+ * paper mentioned above.
+ */
+static double bias_estimate(int precision, double raw_est) {
+ // Determine the samples available
+ int samples;
+ switch (precision) {
+ case 4:
+ samples = 80;
+ break;
+ case 5:
+ samples = 160;
+ break;
+ default:
+ samples = 200;
+ break;
+ }
+
+ // Get the proper arrays based on precision
+ const double *estimates = *(g_rawEstimateData+(precision-4));
+ const double *biases = *(g_biasData+(precision-4));
+
+ // Get the matching biases
+ int idx = binary_search(raw_est, samples, estimates);
+ if (idx == 0)
+ return biases[0];
+ else if (idx == samples)
+ return biases[samples-1];
+ else
+ return (biases[idx] + biases[idx-1]) / 2;
+}
+
+// Computes the raw cardinality estimate
+static double hll_raw_estimate(const uint32_t *registers, unsigned char precision, long long reset_idx, long long time_window_ms, int *num_zero)
+{
+ int m = NUM_REG(precision);
+
+ double Mi=0, Wi=0;//Wi as the time window span of the i-th register Mi.
+ double lambda=0;
+ double harmonic_mean=0;
+ *num_zero=0;
+ int effective_reg=0;
+ for (int i=0; i < m; i++)
+ {
+ Wi=(i>=reset_idx)?(m+reset_idx-i):(reset_idx-i)+1;
+ Mi = get_register(registers, i);
+ if(!Mi)
+ *num_zero += 1;
+ //STHLL++ neglects the 1/8 of the lowest registers to reduce the variance in the final evaluation.
+ if(Wi < m/8 && time_window_ms)
+ {
+ continue;
+ }
+ effective_reg++;
+ lambda=pow(2.0, Mi-1)*m*m/Wi;
+ harmonic_mean += 1/lambda;
+ }
+ harmonic_mean = effective_reg/harmonic_mean;
+ return harmonic_mean;
+}
+double hll_count(const uint32_t *registers, unsigned char precision, long long reset_idx, long long time_window_ms)
+{
+ int num_zero=0;
+ double raw_est=hll_raw_estimate(registers, precision, reset_idx, time_window_ms, &num_zero);
+ raw_est*=alpha(precision);
+ double est=0;
+ double num_reg=NUM_REG(precision);
+ if (raw_est <= 5 * num_reg) {
+ raw_est -= bias_estimate(precision, raw_est);
+ }
+ if(raw_est<=5*num_reg/2)
+ {
+ if(num_zero)
+ {
+ est=linear_count(num_reg, num_zero);
+ assert(est>=0);
+ return est;
+ }
+ }
+ if(raw_est>INT32_MAX/30)
+ {
+ return INT32_MIN*log(1-raw_est/INT32_MAX);
+ }
+ assert(raw_est>=0);
+ return raw_est;
+}
+void hll_reset_registers(uint32_t *registers, unsigned char precision, long long offset, long long count)
+{
+ int num_reg=NUM_REG(precision);
+ if(count>=num_reg)
+ {
+ clear_register(registers, precision);
+ return;
+ }
+ for(int i=0; i<count; i++)
+ {
+ reset_register(registers, (offset+i)%num_reg);
+ }
+ return;
+}
+void hll_advance_reset_index(long long *reset_idx, long long reset_reg_count, unsigned char precision)
+{
+ *reset_idx=(*reset_idx+reset_reg_count)%NUM_REG(precision);
+ return;
+}
+long long hll_get_reset_register_count(unsigned char precision, long long time_window_ms, struct timeval now, struct timeval *reset_time)
+{
+ long long reset_time_slot_us=RESET_TIME_SLOT_US(time_window_ms, precision);
+ long long delta_us=timeval_delta_us((*reset_time), now);
+ if(delta_us < reset_time_slot_us)
+ return 0;
+
+ //We cannot use 'now' as reset_time, but rounded to the nearest time slot.
+ long long reset_bit_count=0;
+ reset_bit_count=delta_us/reset_time_slot_us;
+ struct timeval elapse;
+ elapse.tv_sec=reset_time_slot_us*reset_bit_count/1000/1000;
+ elapse.tv_usec=reset_time_slot_us*reset_bit_count%(1000*1000);
+ timeradd(reset_time, &elapse, reset_time);
+ return reset_bit_count;
+}
+void hll_merge(uint32_t *dst_registers, const uint32_t *src_registers, unsigned char precision)
+{
+ int n_register=NUM_REG(precision);
+ int s_reg=0, d_reg=0;
+ for(int i=0; i<n_register; i++)
+ {
+
+ s_reg=get_register(src_registers, i);
+ d_reg=get_register(dst_registers, i);
+ set_register(dst_registers, i, MAX(s_reg, d_reg));
+ }
+}
+double hll_error(unsigned char precision)
+{
+ /*
+ * Error of HLL is 1.04 / sqrt(m)
+ * m is given by 2^p
+ */
+ return 1.04/sqrt(NUM_REG(precision));
+}
+size_t hll_size(unsigned char precision)
+{
+ // Determine how many registers are needed
+ int num_reg = NUM_REG(precision);
+
+ // Get the full words required
+ int words = INT_CEIL(num_reg, REG_PER_WORD);
+ return sizeof(uint32_t)*words;
+}
+int hll_should_slide(unsigned char precision, long long time_window_ms, struct timeval now, struct timeval reset_time)
+{
+ assert(time_window_ms>0);
+ long long reset_time_slot_us=RESET_TIME_SLOT_US(time_window_ms, precision);
+ long long delta_us=timeval_delta_us(reset_time, now);
+ if(delta_us >= reset_time_slot_us)
+ return 1;
+ return 0;
+}
+
+void hll_encode_into_base64(const uint32_t *registers, unsigned char precision, char **blob, size_t *blob_sz)
+{
+ size_t sz=0, offset=0;
+ size_t num_reg = NUM_REG(precision);
+ size_t words = INT_CEIL(num_reg, REG_PER_WORD);
+
+ sz = hll_size(precision) + 2; // [version][precision][data..., + 2 for version and precision
+ unsigned char *buffer = ALLOC(unsigned char, sz);
+
+ const unsigned char version = 1;
+ memcpy(buffer+offset, &version, 1);
+ offset += 1;
+
+ memcpy(buffer+offset, &precision, 1);
+ offset += 1;
+
+ for (int i = 0; i < words; i++) {
+ uint32_t word = registers[i];
+ buffer[offset++] = word >> 24;
+ buffer[offset++] = word >> 16;
+ buffer[offset++] = word >> 8;
+ buffer[offset++] = word;
+ }
+
+ char *enc = b64_encode(buffer, sz);
+ free(buffer);
+ *blob_sz = strlen(enc);
+ *blob = enc;
+ return;
+} \ No newline at end of file
diff --git a/src/cells/hll_common.h b/src/cells/hll_common.h
new file mode 100644
index 0000000..b62530a
--- /dev/null
+++ b/src/cells/hll_common.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdint.h>
+#include <sys/time.h>//gettimeofday
+#include <stddef.h>
+
+uint32_t *hll_create_register(int precision);
+void hll_free_register(uint32_t *registers);
+uint32_t *hll_duplicate(const uint32_t *registers, unsigned char precision);
+int hll_add_hash(uint32_t *registers, unsigned char precision, uint64_t hash);
+double hll_count(const uint32_t *registers, unsigned char precision, long long reset_idx, long long time_window_ms);
+size_t hll_size(unsigned char precision);
+double hll_error(unsigned char precision);
+int hll_should_slide(unsigned char precision, long long time_window_ms, struct timeval now, struct timeval reset_time);
+void hll_reset_registers(uint32_t *registers, unsigned char precision, long long offset, long long count);
+long long hll_get_reset_register_count(unsigned char precision, long long time_window_ms, struct timeval now, struct timeval *reset_time);
+void hll_advance_reset_index(long long *reset_idx, long long reset_reg_count, unsigned char precision);
+void hll_merge(uint32_t *dst_registers, const uint32_t *src_registers, unsigned char precision);
+void hll_encode_into_base64(const uint32_t *registers, unsigned char precision, char **blob, size_t *blob_sz);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/src/cells/spread_sketch.c b/src/cells/spread_sketch.c
new file mode 100644
index 0000000..85f123b
--- /dev/null
+++ b/src/cells/spread_sketch.c
@@ -0,0 +1,719 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+
+#include "xxhash/xxhash.h"
+#include "uthash.h"
+
+#include "spread_sketch.h"
+#include "hll_common.h"
+#include "exdata.h"
+
+struct entry {
+ int ref_count;
+ void *exdata;
+ bool dying;
+ char *key;
+ size_t key_len;
+ UT_hash_handle hh;
+};
+
+struct spread_sketch_scheme {
+ exdata_new_cb new_fn;
+ exdata_free_cb free_fn;
+ exdata_merge_cb merge_fn;
+ exdata_reset_cb reset_fn;
+ exdata_copy_cb copy_fn;
+};
+
+struct entry_table {
+ struct entry *entry;
+
+ struct spread_sketch_scheme scheme;
+};
+
+struct bucket {
+ struct entry *content;
+ uint32_t level;
+ long long last_update_ms; // linux timestamp since Jan 1st 1970
+ uint32_t *sthll_register;
+};
+
+struct spread_sketch {
+ int depth;
+ int width;
+ long long time_window_ms;
+ unsigned char precision;
+
+ // shared states of all sthlls
+ long long reset_idx;
+ struct timeval reset_time;
+
+ struct spread_sketch_scheme scheme;
+
+ struct bucket *buckets;
+ struct entry_table *table;
+
+ int level0_cnt; // used to filter out dummy adding
+};
+
+static void *default_new_fn(void *arg) {
+ return NULL;
+}
+static void default_free_fn(void *exdata) {
+ return;
+}
+static void default_merge_fn(void *dest, void *src) {
+ return;
+}
+static void default_reset_fn(void *exdata) {
+ return;
+}
+static void *default_copy_fn(void *exdata) {
+ return exdata;
+}
+
+struct spread_sketch_scheme DEFAULT_SCHEME = {
+ .new_fn = default_new_fn,
+ .free_fn = default_free_fn,
+ .merge_fn = default_merge_fn,
+ .reset_fn = default_reset_fn,
+ .copy_fn = default_copy_fn
+};
+
+uint32_t cal_true_level(const struct spread_sketch *ss, int bucket_idx, long long t) {
+ /*
+ return f(t), the actual level of bucket, which satisfy:
+ 1. d 2^f(t)/dt is constants
+ 2. f(t0 + 2T) = 0
+ 3. f((t0) = L )
+ */
+ struct bucket *bucket = &ss->buckets[bucket_idx];
+ if (ss->time_window_ms == 0) {
+ return bucket->level;
+ }
+
+ unsigned L = bucket->level;
+ long long T = ss->time_window_ms;
+ long long t0 = bucket->last_update_ms;
+
+ assert(t >= t0);
+ if (t - t0 >= 2 * T) {
+ return 0;
+ }
+ if (L <= 1) {
+ return t - t0 >= T ? 0 : L;
+ }
+
+ long long tmp_exp = 1 << L;
+ double a = ((double)(1 - tmp_exp)) / (2.0 * T);
+ double b = (double)(tmp_exp);
+
+ return (uint32_t)(log2(a * ((double)(t-t0)) + b) + 0.5);
+}
+
+static inline bool key_equal(const char *key1, size_t key1_len, const char *key2, size_t key2_len) {
+ if (key1_len != key2_len) {
+ return false;
+ }
+ return memcmp(key1, key2, key1_len) == 0;
+}
+static inline char *key_dup(const char *key, size_t key_len) {
+ char *ret = malloc(key_len+1);
+ memcpy(ret, key, key_len);
+ ret[key_len] = '\0';
+ return ret;
+}
+
+struct entry *smart_ptr_table_find(struct entry_table *table, const char *key, size_t key_len) {
+ struct entry *ret;
+ HASH_FIND(hh, table->entry, key, key_len, ret);
+ if (ret == NULL || ret->dying) {
+ return NULL;
+ }
+ return ret;
+}
+
+struct entry *smart_ptr_table_get(struct entry_table *table, const char *key, size_t key_len, void *arg) {
+ struct entry *ret;
+ HASH_FIND(hh, table->entry, key, key_len, ret);
+
+ if (ret != NULL) {
+ ret->dying = false;
+ ret->ref_count++;
+ } else {
+ ret = malloc(sizeof(struct entry));
+ ret->dying = false;
+ ret->ref_count = 1;
+ ret->key = key_dup(key, key_len);
+ ret->key_len = key_len;
+ if (arg == NULL) {
+ ret->exdata = NULL;
+ } else {
+ ret->exdata = table->scheme.new_fn(arg);
+ }
+ HASH_ADD_KEYPTR(hh, table->entry, ret->key, ret->key_len, ret);
+ }
+
+ return ret;
+}
+
+int smart_ptr_table_release(struct entry_table *table, const char *key, size_t key_len) {
+ struct entry *ret;
+ HASH_FIND(hh, table->entry, key, key_len, ret);
+ if (ret == NULL) {
+ return -1;
+ }
+
+ ret->ref_count--;
+ if (ret->ref_count == 0) {
+ // printf("release %s\n", key);
+ HASH_DEL(table->entry, ret);
+ table->scheme.free_fn(ret->exdata);
+ free(ret->key);
+ free(ret);
+ }
+
+ return 0;
+}
+
+void smart_ptr_table_free(struct entry_table *table) {
+ struct entry *current, *tmp;
+ HASH_ITER(hh, table->entry, current, tmp) {
+ HASH_DEL(table->entry, current);
+ table->scheme.free_fn(current->exdata);
+ free(current->key);
+ free(current);
+ }
+ free(table);
+}
+
+struct entry_table *smart_ptr_table_new() {
+ struct entry_table *table = malloc(sizeof(struct entry_table));
+ table->entry = NULL;
+
+ table->scheme = DEFAULT_SCHEME;
+
+ return table;
+}
+
+struct spread_sketch *spread_sketch_new(int depth, int width, unsigned char precision, int time_window_ms, struct timeval now) {
+ struct spread_sketch *pthis = malloc(sizeof(struct spread_sketch));
+
+ pthis->depth = depth;
+ pthis->width = width;
+ pthis->time_window_ms = time_window_ms;
+ pthis->precision = precision;
+
+ pthis->reset_idx = 0;
+ pthis->reset_time = now;
+
+ pthis->buckets = calloc(depth * width, sizeof(struct bucket));
+ pthis->scheme = DEFAULT_SCHEME;
+ pthis->table = smart_ptr_table_new();
+ pthis->table->scheme = pthis->scheme;
+
+ for (int i = 0; i < depth * width; i++) {
+ pthis->buckets[i].sthll_register = hll_create_register(precision);
+ }
+
+ pthis->level0_cnt = pthis->depth * pthis->width;
+
+ return pthis;
+}
+
+void move_registers_forward(struct spread_sketch *ss, const struct timeval *now) {
+ if (ss->time_window_ms == 0) {
+ return;
+ }
+
+ long long reset_reg_count = hll_get_reset_register_count(ss->precision, ss->time_window_ms, *now, &ss->reset_time);
+ if (reset_reg_count > 0) {
+ for (int i = 0; i < ss->depth * ss->width; i++) {
+ hll_reset_registers(ss->buckets[i].sthll_register, ss->precision, ss->reset_idx, reset_reg_count);
+ }
+ hll_advance_reset_index(&ss->reset_idx, reset_reg_count, ss->precision);
+ }
+}
+
+// return 0 if not added, return 1 if added
+int spread_sketch_add_hash(struct spread_sketch *ss, const char *key, size_t key_length, uint64_t item_hash, void *arg, struct timeval now) {
+ uint32_t level = (uint32_t)__builtin_clzll(item_hash) + 1;
+ long long now_ms = now.tv_sec * 1000 + now.tv_usec / 1000;
+
+ if (item_hash == DUMMY_ITEM_HASH) {
+ if (ss->level0_cnt == 0) {
+ return 0;
+ }
+ assert(ss->level0_cnt>0);
+ }
+
+ // https://www.eecs.harvard.edu/~michaelm/postscripts/tr-02-05.pdf
+ // A technique from the hashing literature is to use two hash functions h1(x) and h2(x) to simulate additional hash functions of the form gi(x) = h1(x) + ih2(x)
+ // Assuming that the 128-bit xxhash function is perfect, we can view it as a combination of two 64-bit hash functions.
+ uint64_t hash_x_tmp = XXH3_64bits_withSeed(key, key_length, 171);
+ uint32_t hash_x1 = (uint32_t) (hash_x_tmp >> 32);
+ uint32_t hash_x2 = (uint32_t) hash_x_tmp;
+
+ bool in_sketch = false;
+ move_registers_forward(ss, &now);
+ for (int i = 0; i < ss->depth; i++) {
+ uint32_t hash_x = hash_x1 + i * hash_x2;
+ int bucket_idx = (hash_x % ss->width) + i * ss->width;
+ struct bucket *bucket = &ss->buckets[bucket_idx];
+
+ if (bucket->content != NULL && key_equal(bucket->content->key, bucket->content->key_len, key, key_length)) {
+ bucket->content->dying = false;
+ bucket->last_update_ms = now_ms;
+
+ if (bucket->level < level) {
+ if (bucket->level == 0) {
+ ss->level0_cnt--;
+ }
+ bucket->level = level;
+ }
+ in_sketch = true;
+ } else {
+ uint32_t true_level = bucket->content == NULL ? 0: cal_true_level(ss, bucket_idx, now_ms);
+
+ if (level > true_level) {
+ if (bucket->level == 0) {
+ ss->level0_cnt--;
+ }
+
+ // printf("update key %s to %s, and level %u to %u, in bucket (r,w)=(%d,%u)\n", bucket->content == NULL ? "NULL": bucket->content->key, key, true_level, level, i, hash_x % ss->width);
+ const struct entry *content_old = bucket->content;
+ if (content_old != NULL) {
+ smart_ptr_table_release(ss->table, content_old->key, content_old->key_len);
+ }
+ struct entry *content_new = smart_ptr_table_get(ss->table, key, key_length, arg);
+ bucket->content = content_new;
+
+ bucket->last_update_ms = now_ms;
+ bucket->level = level;
+
+ in_sketch = true;
+ }
+ }
+ hll_add_hash(bucket->sthll_register, ss->precision, item_hash);
+ }
+
+ return in_sketch ? 1 : 0;
+}
+
+int spread_sketch_add(struct spread_sketch *ss, const char *key, size_t key_length, const char* item, size_t item_len, void *arg, struct timeval now) {
+ uint64_t hash = XXH3_64bits(item, item_len);
+ return spread_sketch_add_hash(ss, key, key_length, hash, arg, now);
+}
+
+double spread_sketch_point_query(const struct spread_sketch *ss, const char *key, size_t key_length) {
+ uint64_t hash_x_tmp = XXH3_64bits_withSeed(key, key_length, 171);
+ uint32_t hash_x1 = (uint32_t) (hash_x_tmp >> 32);
+ uint32_t hash_x2 = (uint32_t) hash_x_tmp;
+
+ double count_min = (double)INT32_MAX;
+ for (int i = 0; i < ss->depth; i++) {
+ uint32_t hash_x = hash_x1 + i * hash_x2;
+ int bucket_idx = (hash_x % ss->width) + i * ss->width;
+
+ double est = hll_count(ss->buckets[bucket_idx].sthll_register, ss->precision, ss->reset_idx, ss->time_window_ms);
+ if (est < count_min) {
+ count_min = est;
+ }
+ }
+ return count_min;
+}
+
+struct spread_sketch *duplicate_and_step(const struct spread_sketch *ss, const struct timeval *now) {
+ struct spread_sketch *duplicate = malloc(sizeof(struct spread_sketch));
+ duplicate->depth = ss->depth;
+ duplicate->width = ss->width;
+ duplicate->precision = ss->precision;
+ duplicate->reset_idx = ss->reset_idx;
+ duplicate->reset_time = ss->reset_time;
+ duplicate->time_window_ms = ss->time_window_ms;
+ duplicate->buckets = calloc(ss->depth * ss->width, sizeof(struct bucket));
+ for (int i = 0; i < ss->depth * ss->width; i++) {
+ duplicate->buckets[i].sthll_register = hll_duplicate(ss->buckets[i].sthll_register, ss->precision);
+ }
+
+ move_registers_forward(duplicate, now);
+ return duplicate;
+}
+
+double spread_sketch_query(const struct spread_sketch *ss, const char *key, size_t key_length, struct timeval now) {
+ if (ss->time_window_ms!=0 && hll_should_slide(ss->precision, ss->time_window_ms, now, ss->reset_time)) {
+ struct spread_sketch *duplicate = duplicate_and_step(ss, &now);
+ double ret = spread_sketch_point_query(duplicate, key, key_length);
+ spread_sketch_free(duplicate);
+ return ret;
+ } else {
+ return spread_sketch_point_query(ss, key, key_length);
+ }
+}
+
+void spread_sketch_free(struct spread_sketch *ss) {
+ if (ss == NULL) {
+ return;
+ }
+ smart_ptr_table_free(ss->table);
+ for (int i = 0; i < ss->depth * ss->width; i++) {
+ hll_free_register(ss->buckets[i].sthll_register);
+ }
+ free(ss->buckets);
+ free(ss);
+}
+
+void spread_sketch_merge(struct spread_sketch *dst, const struct spread_sketch *src)
+{
+ assert(dst->depth == src->depth && dst->width == src->width);
+ assert(dst->time_window_ms == src->time_window_ms);
+ assert(dst->precision == src->precision);
+
+ for (int i = 0; i < dst->depth * dst->width; i++) {
+ const struct bucket *bucket_src = &src->buckets[i];
+ struct bucket *bucket_dst = &dst->buckets[i];
+
+ if (bucket_src->content == NULL || bucket_src->content->dying) {
+ continue;
+ }
+
+ hll_merge(bucket_dst->sthll_register, src->buckets[i].sthll_register, dst->precision);
+ if (bucket_dst->level == 0 && bucket_src->level > 0) {
+ dst->level0_cnt--;
+ }
+
+ if (bucket_dst->content == NULL) {
+ bucket_dst->content = smart_ptr_table_get(dst->table, bucket_src->content->key, bucket_src->content->key_len, NULL);
+ bucket_dst->level = bucket_src->level;
+ bucket_dst->last_update_ms = bucket_src->last_update_ms;
+
+ continue;
+ }
+ bucket_dst->content->dying = false;
+
+ if (key_equal(bucket_src->content->key, bucket_src->content->key_len, bucket_dst->content->key, bucket_dst->content->key_len)) {
+ if (bucket_src->level > bucket_dst->level) {
+ bucket_dst->level = bucket_src->level;
+ }
+ if (bucket_src->last_update_ms > bucket_dst->last_update_ms) {
+ bucket_dst->last_update_ms = bucket_src->last_update_ms;
+ }
+ } else {
+ uint32_t true_level_src = cal_true_level(src, i, bucket_src->last_update_ms);
+ uint32_t true_level_dst = cal_true_level(dst, i, bucket_dst->last_update_ms);
+
+ if (true_level_src > true_level_dst) {
+ smart_ptr_table_release(dst->table, bucket_dst->content->key, bucket_dst->content->key_len);
+ bucket_dst->content = smart_ptr_table_get(dst->table, bucket_src->content->key, bucket_src->content->key_len, NULL);
+ bucket_dst->last_update_ms = bucket_src->last_update_ms;
+ }
+ }
+ }
+
+ // for exdata
+ const struct spread_sketch_scheme *scheme = &dst->table->scheme;
+
+ struct entry *content_dest, *content_src, *tmp;
+ HASH_ITER(hh, dst->table->entry, content_dest, tmp) {
+ HASH_FIND(hh, src->table->entry, content_dest->key, content_dest->key_len, content_src);
+ if (content_src == NULL || content_src->dying) {
+ continue;
+ }
+
+ if (content_dest->exdata == NULL) {
+ content_dest->exdata = scheme->copy_fn(content_src->exdata);
+ } else {
+ scheme->merge_fn(content_dest->exdata, content_src->exdata);
+ }
+ }
+}
+
+void *spread_sketch_get0_exdata(const struct spread_sketch *ss, const char *key, size_t key_len) {
+ const struct entry *content = smart_ptr_table_find(ss->table, key, key_len);
+ if (content == NULL) {
+ return NULL;
+ }
+ return content->exdata;
+}
+
+void spread_sketch_reset(struct spread_sketch *ss) {
+ for (int i = 0; i < ss->depth * ss->width; i++) {
+ struct bucket *bucket = &ss->buckets[i];
+ bucket->level = 0;
+ hll_reset_registers(bucket->sthll_register, ss->precision, ss->reset_idx, 100000); // count is big enough
+ }
+
+ struct entry *content, *tmp;
+ HASH_ITER(hh, ss->table->entry, content, tmp) {
+ ss->scheme.reset_fn(content->exdata);
+ content->dying = true;
+ }
+
+ ss->level0_cnt = ss->depth * ss->width;
+}
+
+void spread_sketch_set_exdata_schema(struct spread_sketch *ss, exdata_new_cb new_fn, exdata_free_cb free_fn, exdata_merge_cb merge_fn, exdata_reset_cb reset_fn, exdata_copy_cb copy_fn) {
+ ss->scheme.new_fn = new_fn;
+ ss->scheme.free_fn = free_fn;
+ ss->scheme.merge_fn = merge_fn;
+ ss->scheme.reset_fn = reset_fn;
+ ss->scheme.copy_fn = copy_fn;
+
+ ss->table->scheme = ss->scheme;
+}
+
+int spread_sketch_get_count(const struct spread_sketch *ss) {
+ int cnt = 0;
+ struct entry *content, *tmp;
+ HASH_ITER(hh, ss->table->entry, content, tmp) {
+ if (!content->dying) {
+ cnt++;
+ }
+ }
+
+ return cnt;
+}
+
+size_t spread_sketch_list(const struct spread_sketch *ss, void **exdatas, size_t n_exdatas) {
+ size_t count = 0;
+ struct entry *content, *tmp;
+ HASH_ITER(hh, ss->table->entry, content, tmp) {
+ if (content->dying) {
+ continue;
+ }
+ if (count >= n_exdatas) {
+ break;
+ }
+ exdatas[count] = content->exdata;
+ count++;
+ }
+ return count;
+}
+
+void spread_sketch_list_keys(const struct spread_sketch *ss, char ***keys, size_t **key_lens, size_t *n_keys) {
+ size_t count_max = HASH_COUNT(ss->table->entry);
+ size_t count = 0;
+
+ *keys = malloc(count_max * sizeof(char *));
+ *key_lens = malloc(count_max * sizeof(size_t));
+
+ struct entry *content, *tmp;
+ HASH_ITER(hh, ss->table->entry, content, tmp) {
+ if (content->dying) {
+ continue;
+ }
+
+ (*keys)[count] = content->key;
+ (*key_lens)[count] = content->key_len;
+ count++;
+ }
+
+ *n_keys = count;
+}
+
+double spread_sketch_get_cardinality(const struct spread_sketch *ss, const char *key, size_t key_len) {
+ if (smart_ptr_table_find(ss->table, key, key_len) == NULL) {
+ return -1;
+ }
+
+ double est = spread_sketch_point_query(ss, key, key_len);
+ return est;
+}
+
+char *spread_sketch_get_hll_base64_serialization(const struct spread_sketch *ss, const char *key, size_t key_len) {
+ uint64_t hash_x_tmp = XXH3_64bits_withSeed(key, key_len, 171);
+ uint32_t hash_x1 = (uint32_t) (hash_x_tmp >> 32);
+ uint32_t hash_x2 = (uint32_t) hash_x_tmp;
+ uint32_t *register_ret = NULL;
+ double min_cardinality = (double)INT32_MAX;
+
+ for (int i = 0; i < ss->depth; i++) {
+ uint32_t hash_x = hash_x1 + i * hash_x2;
+ int bucket_idx = (hash_x % ss->width) + i * ss->width;
+
+ double est = hll_count(ss->buckets[bucket_idx].sthll_register, ss->precision, ss->reset_idx, ss->time_window_ms);
+ if (est < min_cardinality) {
+ min_cardinality = est;
+ register_ret = ss->buckets[bucket_idx].sthll_register;
+ }
+ }
+
+ char *enc;
+ size_t enc_len;
+ hll_encode_into_base64(register_ret, ss->precision, &enc, &enc_len);
+ return enc;
+}
+
+struct spread_sketch *spread_sketch_copy(const struct spread_sketch *src) {
+ struct spread_sketch *dst = malloc(sizeof(struct spread_sketch));
+ memcpy(dst, src, sizeof(struct spread_sketch));
+
+ dst->buckets = calloc(dst->depth * dst->width, sizeof(struct bucket));
+ dst->table = smart_ptr_table_new();
+ spread_sketch_set_exdata_schema(dst, src->scheme.new_fn, src->scheme.free_fn, src->scheme.merge_fn, src->scheme.reset_fn, src->scheme.copy_fn);
+ for (int i = 0; i < dst->depth * dst->width; i++) {
+ dst->buckets[i].sthll_register = hll_duplicate(src->buckets[i].sthll_register, src->precision);
+ }
+
+ for (int i = 0; i < dst->depth * dst->width; i++) {
+ if (src->buckets[i].content == NULL || src->buckets[i].content->dying) {
+ continue;
+ }
+ dst->buckets[i].content = smart_ptr_table_get(dst->table, src->buckets[i].content->key, src->buckets[i].content->key_len, NULL);
+ dst->buckets[i].level = src->buckets[i].level;
+ if (dst->buckets[i].content->exdata == NULL) {
+ dst->buckets[i].content->exdata = src->scheme.copy_fn(src->buckets[i].content->exdata);
+ }
+ }
+ return dst;
+}
+
+void spread_sketch_get_parameter_recommendation(int expected_super_spreader_number, int *depth_out, int *width_out, unsigned char *precision_out)
+{
+ int logk = expected_super_spreader_number >= 3200 ? 4 : 3; // lg3200 = 3.51,round up to 4
+ *depth_out = logk;
+
+ int w;
+ if (expected_super_spreader_number <= 100) {
+ w = expected_super_spreader_number * 3 /2; // * 1.5, when the number is small, we need more width
+ } else {
+ w = expected_super_spreader_number + 50; // + 50: 100*1.5-100 = 50, make w=f(k) continuous
+ }
+ if (w < 40) {
+ w = 40;
+ }
+ *width_out = w;
+
+ *precision_out = 6;
+}
+
+void spread_sketch_get_parameter(const struct spread_sketch *ss, int *depth_out, int *width_out, unsigned char *precision_out, int *time_window_ms_out)
+{
+ *depth_out = ss->depth;
+ *width_out = ss->width;
+ *precision_out = ss->precision;
+ *time_window_ms_out = ss->time_window_ms;
+}
+
+void spread_sketch_change_precision(struct spread_sketch *ss, unsigned char precision) {
+ for (int i = 0; i < ss->depth * ss->width; i++) {
+ hll_free_register(ss->buckets[i].sthll_register);
+ ss->buckets[i].sthll_register = hll_create_register(precision);
+ }
+ ss->precision = precision;
+}
+
+void spread_sketch_serialize(const struct spread_sketch *ss, char **blob, size_t *blob_sz)
+{
+ /*
+ format:
+ struct spread_sketch(including useless pointers)
+ struct bucket * depth * width
+ int64_t * depth * width for keylen + key(close-knit, key_len, key, key_len, key, ..., in order of bucket index)
+ */
+ // get serialize size
+
+ size_t sz = 0;
+ sz += sizeof(struct spread_sketch);
+ sz += ss->depth * ss->width * (sizeof(struct bucket) + hll_size(ss->precision) + sizeof(int64_t));
+ for (int i = 0; i < ss->depth * ss->width; i++) {
+ if (ss->buckets[i].content != NULL) {
+ sz += ss->buckets[i].content->key_len;
+ }
+ }
+
+ char *buffer = malloc(sz);
+ *blob = buffer;
+ *blob_sz = sz;
+
+ memcpy(buffer, ss, sizeof(struct spread_sketch));
+ buffer += sizeof(struct spread_sketch);
+ for (int i = 0; i < ss->depth * ss->width; i++) {
+ const struct bucket *bucket = &ss->buckets[i];
+ memcpy(buffer, bucket, sizeof(struct bucket));
+ buffer += sizeof(struct bucket);
+ memcpy(buffer, bucket->sthll_register, hll_size(ss->precision));
+ buffer += hll_size(ss->precision);
+ }
+ for (int i = 0; i < ss->depth * ss->width; i++) {
+ int64_t key_len;
+ if (ss->buckets[i].content != NULL) {
+ key_len = ss->buckets[i].content->key_len;
+ } else {
+ key_len = 0;
+ }
+ memcpy(buffer, &key_len, sizeof(int64_t));
+ buffer += sizeof(int64_t);
+
+ if (key_len > 0) {
+ memcpy(buffer, ss->buckets[i].content->key, key_len);
+ buffer += key_len;
+ }
+ }
+}
+
+struct spread_sketch *spread_sketch_deserialize(const char *blob, size_t blob_sz)
+{
+ struct spread_sketch *ss = malloc(sizeof(struct spread_sketch));
+ memcpy(ss, blob, sizeof(struct spread_sketch));
+ blob += sizeof(struct spread_sketch);
+ ss->table = smart_ptr_table_new();
+ ss->buckets = calloc(ss->depth * ss->width, sizeof(struct bucket));
+ ss->scheme = DEFAULT_SCHEME;
+
+ for (int i = 0; i < ss->depth * ss->width; i++) {
+ struct bucket *bucket = &ss->buckets[i];
+ memcpy(bucket, blob, sizeof(struct bucket));
+ blob += sizeof(struct bucket);
+
+ bucket->sthll_register = hll_create_register(ss->precision);
+ memcpy(bucket->sthll_register, blob, hll_size(ss->precision));
+ blob += hll_size(ss->precision);
+ }
+
+ for (int i = 0; i < ss->depth * ss->width; i++) {
+ int64_t key_len;
+ memcpy(&key_len, blob, sizeof(int64_t));
+ blob += sizeof(int64_t);
+ if (key_len == 0) {
+ continue;
+ }
+ const char *key = blob;
+ blob += key_len;
+
+ struct entry *content = smart_ptr_table_get(ss->table, key, key_len, NULL);
+ ss->buckets[i].content = content;
+ }
+
+ return ss;
+}
+
+void spread_sketch_merge_blob(struct spread_sketch *dst, const char *blob, size_t blob_sz)
+{
+ struct spread_sketch *src = spread_sketch_deserialize(blob, blob_sz);
+ spread_sketch_merge(dst, src);
+ spread_sketch_free(src);
+}
+
+size_t spread_sketch_calculate_memory_usage(const struct spread_sketch *ss)
+{
+ size_t ret = 0;
+ ret += sizeof(struct spread_sketch);
+
+ size_t bucket_size = sizeof(struct bucket) + hll_size(ss->precision);
+ printf("every bucket size: %zu\n", bucket_size);
+ ret += ss->depth * ss->width * bucket_size;
+
+ printf("the number of content: %u\n", HASH_COUNT(ss->table->entry));
+ struct entry *content, *tmp;
+ HASH_ITER(hh, ss->table->entry, content, tmp) {
+ ret += sizeof(struct entry);
+ ret += content->key_len;
+ // assume the exdata is NULL
+ }
+ return ret;
+} \ No newline at end of file
diff --git a/src/cells/spread_sketch.h b/src/cells/spread_sketch.h
new file mode 100644
index 0000000..c15ad81
--- /dev/null
+++ b/src/cells/spread_sketch.h
@@ -0,0 +1,55 @@
+#pragma once
+
+
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#include "exdata.h"
+#include <stdint.h> //uint64_t
+#include <sys/time.h> // struct timeval
+#include <stddef.h>
+
+#define DUMMY_ITEM_HASH (1ULL<<63) // level(left most zeros) = 0
+
+struct spread_sketch;
+
+// set time_window_ms to zero, the spread sketch will not slide and decay, in which case, `now` can be any value
+struct spread_sketch *spread_sketch_new(int depth, int width, unsigned char precision, int time_window_ms, struct timeval now);
+void spread_sketch_free(struct spread_sketch *ss);
+void spread_sketch_set_exdata_schema(struct spread_sketch *ss, exdata_new_cb new_fn, exdata_free_cb free_fn, exdata_merge_cb merge_fn, exdata_reset_cb reset_fn, exdata_copy_cb copy_fn);
+
+int spread_sketch_add_hash(struct spread_sketch *ss, const char *key, size_t key_length, uint64_t item_hash, void *arg, struct timeval now);
+int spread_sketch_add(struct spread_sketch *ss, const char *key, size_t key_length, const char* item, size_t item_len, void *arg, struct timeval now);
+
+// get the number of keys stored in spread sketch
+int spread_sketch_get_count(const struct spread_sketch *ss);
+// list all the keys in spread sketch. User should free the arrays, but do not free the elements of strings in the array(because they are references to the internal data structure)
+// Example: char **key; size_t *key_len; size_t n_keys; spread_sketch_list_keys(&key, &key_len, &n_keys); free(key); free(key_len);
+void spread_sketch_list_keys(const struct spread_sketch *ss, char ***keys, size_t **key_lens, size_t *n_keys);
+// query the cardinality(or fanout) of a key in spread sketch.
+// Even thought spread sketch algorithm does not requires keys to exist innately, when querying a key that is not present in the spread sketch, `spread_sketch_get_cardinality` will return -1.
+double spread_sketch_get_cardinality(const struct spread_sketch *ss, const char *key, size_t key_len);
+// query a hyperloglog 's base64 serialization. The serialization format is [1,precision,register...] and then encoded by base64
+char *spread_sketch_get_hll_base64_serialization(const struct spread_sketch *ss, const char *key, size_t key_len);
+void *spread_sketch_get0_exdata(const struct spread_sketch *ss, const char *key, size_t key_len);
+// in most cases, it has the same output as `spread_sketch_get_cardinality`, but it will perform more like an ordinary spread sketch query.
+// Will always return a value, even if the key is not present in the spread sketch. Must pass a `now` value required by Stagger hll query.
+double spread_sketch_query(const struct spread_sketch *ss, const char *key, size_t key_length, struct timeval now);
+
+void spread_sketch_merge(struct spread_sketch *dest, const struct spread_sketch *src);
+struct spread_sketch *spread_sketch_copy(const struct spread_sketch *src);
+void spread_sketch_serialize(const struct spread_sketch *ss, char **blob, size_t *blob_sz);
+struct spread_sketch *spread_sketch_deserialize(const char *blob, size_t blob_sz);
+void spread_sketch_merge_blob(struct spread_sketch *dst, const char *blob, size_t blob_sz);
+void spread_sketch_reset(struct spread_sketch *ss);
+
+void spread_sketch_get_parameter(const struct spread_sketch *ss, int *depth_out, int *width_out, unsigned char *precision_out, int *time_window_ms_out);
+// spread sketch alway store values more than expected_query_num,expected_query_num is a hint to set spread sketch parameters properly
+void spread_sketch_get_parameter_recommendation(int expected_super_spreader_number, int *depth_out, int *width_out, unsigned char *precision_out);
+size_t spread_sketch_calculate_memory_usage(const struct spread_sketch *ss);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/src/cube.c b/src/cube.c
new file mode 100644
index 0000000..9dc7ab3
--- /dev/null
+++ b/src/cube.c
@@ -0,0 +1,1393 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "uthash.h"
+#define XXH_INLINE_ALL
+#include "xxhash/xxhash.h"
+
+#include "cube.h"
+#include "metric_manifest.h"
+#include "metric.h"
+#include "heavy_keeper.h"
+#include "hash_table.h"
+#include "spread_sketch.h"
+
+#define DEFAULT_N_METRIC 32
+#define DEFAULT_N_CUBE 64
+static const struct timeval DUMMY_TIME_VAL = {0, 0};
+
+struct exdata_new_args {
+ const struct field *cell_dimensions;
+ size_t n_dimensions;
+};
+
+struct cube_manager {
+ struct cube *hash_table; // the key of cube is serialized cube dimensions
+
+ struct cube **cube_slots;
+ size_t next_index; // next_index
+ size_t slots_number;
+};
+
+struct cell {
+ struct metric **slots;
+ size_t next_index; //index of next available slot
+ size_t slots_number;
+ struct field_list cell_dimensions;
+};
+
+struct cube {
+ enum sampling_mode sampling_mode;
+ union {
+ struct heavy_keeper *heavykeeper;
+ struct hash_table *table;
+ struct spread_sketch *spread_sketch;
+ };
+ size_t max_n_cell;
+ struct metric_manifest_manager *manifest_manager;
+
+ struct field *cube_dimensions;
+ size_t n_dimensions;
+
+ int primary_metric_id;
+ char *serialized_dimensions; // the key of cube is serialized cube dimensions
+ size_t serialized_dimensions_len;
+ int id;
+ UT_hash_handle hh;
+};
+
+static struct field *field_array_duplicate(const struct field *fields_src, size_t n_field)
+{
+ struct field *ret = malloc(sizeof(struct field) * n_field);
+
+ for (size_t i = 0; i < n_field; i++) {
+ ret[i].key = strdup(fields_src[i].key);
+ ret[i].type = fields_src[i].type;
+ switch (fields_src[i].type)
+ {
+ case FIELD_VALUE_INTEGER:
+ ret[i].value_longlong = fields_src[i].value_longlong;
+ break;
+ case FIELD_VALUE_CSTRING:
+ ret[i].value_str = strdup(fields_src[i].value_str);
+ break;
+ case FIELD_VALUE_DOUBLE:
+ ret[i].value_double = fields_src[i].value_double;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
+
+void add_cube_to_position(struct cube_manager *pthis, struct cube *cube, int id)
+{
+ if (id >= pthis->slots_number) {
+ struct cube **old_cube_arr = pthis->cube_slots;
+ pthis->cube_slots = calloc(pthis->slots_number * 2, sizeof(struct cube *));
+ memcpy(pthis->cube_slots, old_cube_arr, sizeof(struct cube *) * pthis->slots_number);
+ free(old_cube_arr);
+
+ pthis->slots_number *= 2;
+ }
+ pthis->cube_slots[id] = cube;
+
+ if (id >= pthis->next_index) {
+ pthis->next_index = id + 1;
+ }
+}
+
+void cube_manager_free(struct cube_manager *pthis) {
+ struct cube *node = NULL;
+ struct cube *tmp = NULL;
+ struct cube *head = pthis->hash_table;
+ HASH_ITER(hh, head, node, tmp) {
+ HASH_DEL(head, node);
+ cube_free(node);
+ }
+
+ free(pthis->cube_slots);
+ free(pthis);
+}
+
+struct cube_manager *cube_manager_new() {
+ struct cube_manager *pthis = (struct cube_manager *)malloc(sizeof(struct cube_manager));
+ pthis->hash_table = NULL;
+
+ pthis->cube_slots = (struct cube **)calloc(DEFAULT_N_CUBE, sizeof(struct cube *));
+ pthis->next_index = 0;
+ pthis->slots_number = DEFAULT_N_CUBE;
+ return pthis;
+}
+
+void print_field_array(const struct field *fields, size_t n_field)
+{
+ printf("dimension with length %zu \n", n_field);
+ for (size_t i = 0; i < n_field; i++) {
+ printf("%s: ", fields[i].key);
+ switch (fields[i].type)
+ {
+ case FIELD_VALUE_INTEGER:
+ printf("%lld\n", fields[i].value_longlong);
+ break;
+ case FIELD_VALUE_DOUBLE:
+ printf("%lf\n", fields[i].value_double);
+ break;
+ case FIELD_VALUE_CSTRING:
+ printf("%s\n", fields[i].value_str);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void field_array_to_key(const struct field fields[], size_t n_fields, char **out_key, size_t *out_key_size)
+{
+ if (n_fields == 0) {
+ // use a default dummy key
+ *out_key = strdup("\a\tN");
+ *out_key_size = strlen(*out_key);
+ return;
+ }
+ int i = 0;
+ size_t used_len = 0;
+ struct field *field = NULL;
+
+ size_t alloced_every_time = 1024;
+ size_t remain_key_size = 1024;
+ size_t total_key_size = 1024;
+ char *dynamic_mem = (char *)malloc(total_key_size);
+ void *val_position = NULL;
+
+ size_t key_len = 0;
+ size_t val_len = 0;
+
+ for(i = 0; i < (int)n_fields; i++)
+ {
+ field = (struct field *)&fields[i];
+ key_len = strlen(field->key);
+ switch(field->type)
+ {
+ case FIELD_VALUE_INTEGER:
+ val_len = sizeof(long long);
+ val_position = (void *)&field->value_longlong;
+ break;
+ case FIELD_VALUE_DOUBLE:
+ val_len = sizeof(double);
+ val_position = (void *)&field->value_double;
+
+ break;
+ case FIELD_VALUE_CSTRING:
+ val_len = strlen(field->value_str);
+ val_position = (void *)field->value_str;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ used_len = key_len + val_len;
+ while (used_len >= remain_key_size) {
+ total_key_size += alloced_every_time;
+ remain_key_size += alloced_every_time;
+ dynamic_mem = (char *)realloc(dynamic_mem, total_key_size);
+ }
+ memcpy(dynamic_mem + total_key_size - remain_key_size, field->key, key_len);
+ memcpy(dynamic_mem + total_key_size - remain_key_size + key_len, val_position, val_len);
+ remain_key_size -= used_len;
+ }
+
+ *out_key = dynamic_mem;
+ *out_key_size = total_key_size - remain_key_size;
+}
+
+int cube_manager_add(struct cube_manager *pthis, struct cube *cube)
+{
+ char *key = cube->serialized_dimensions;
+ size_t key_len = cube->serialized_dimensions_len;
+
+ struct cube *old_cube = NULL;
+ HASH_FIND(hh, pthis->hash_table, key, key_len, old_cube);
+ if (old_cube != NULL) {
+ return -1;
+ }
+
+ int id = 0;
+ for ( ;id < pthis->next_index; id++) {
+ if (pthis->cube_slots[id] == NULL) {
+ break;
+ }
+ }
+
+ cube->id = id;
+ HASH_ADD_KEYPTR(hh, pthis->hash_table, cube->serialized_dimensions, key_len, cube);
+
+ add_cube_to_position(pthis, cube, id);
+
+ return id;
+}
+
+void cube_manager_delete(struct cube_manager *pthis, struct cube *cube)
+{
+ int id = cube->id;
+ HASH_DEL(pthis->hash_table, cube);
+
+ cube_free(cube);
+ pthis->cube_slots[id] = NULL;
+ if (id == pthis->next_index - 1) {
+ pthis->next_index--;
+ }
+}
+
+int cube_manager_find(const struct cube_manager *pthis, const struct field *cube_dimensions, size_t n_dimension)
+{
+ char *key;
+ size_t key_len;
+ field_array_to_key(cube_dimensions, n_dimension, &key, &key_len);
+
+ struct cube *node = NULL;
+ HASH_FIND(hh, pthis->hash_table, key, key_len, node);
+
+ free(key);
+ if (node == NULL) {
+ return -1;
+ } else {
+ return node->id;
+ }
+}
+
+struct cube *cube_manager_get_cube_by_id(const struct cube_manager *manager, int cube_id) {
+ if (cube_id < 0 || cube_id >= manager->slots_number) {
+ return NULL;
+ }
+ return manager->cube_slots[cube_id];
+}
+
+void cube_manager_list(const struct cube_manager *pthis, int **cube_ids, int *n_cube) {
+ int all_available_cube_count = 0;
+
+ int *tmp_ids = (int *)malloc(sizeof(int) * pthis->next_index);
+ for (int i = 0; i < pthis->next_index; i++) {
+ if (pthis->cube_slots[i] != NULL) {
+ tmp_ids[all_available_cube_count++] = i;
+ }
+ }
+ if (all_available_cube_count == 0) {
+ free(tmp_ids);
+ *cube_ids = NULL;
+ *n_cube = 0;
+ return;
+ }
+
+ *cube_ids = tmp_ids;
+ *n_cube = all_available_cube_count;
+}
+
+void cube_manager_calibrate(struct cube_manager *pthis, const struct cube_manager *master)
+{
+ struct cube *node_in_master, *node_in_dest, *tmp;
+
+ HASH_ITER(hh, pthis->hash_table, node_in_dest, tmp) {
+ HASH_FIND(hh, master->hash_table, node_in_dest->serialized_dimensions, node_in_dest->serialized_dimensions_len, node_in_master);
+
+ if (node_in_master == NULL) { // exist in self but not in master
+ cube_manager_delete(pthis, node_in_dest);
+ } else {
+ metric_manifest_manager_free(node_in_dest->manifest_manager);
+ node_in_dest->manifest_manager = metric_manifest_manager_copy(node_in_master->manifest_manager);
+ }
+ }
+
+ // exist in master but not in self
+ HASH_ITER(hh, master->hash_table, node_in_master, tmp) {
+ HASH_FIND(hh, pthis->hash_table, node_in_master->serialized_dimensions, node_in_master->serialized_dimensions_len, node_in_dest);
+
+ if (node_in_dest == NULL) {
+ cube_manager_add(pthis, cube_fork(node_in_master));
+ }
+ }
+}
+
+struct cube_manager *cube_manager_fork(const struct cube_manager *src)
+{
+ struct cube_manager *pthis = cube_manager_new();
+ struct cube *node = NULL;
+ struct cube *tmp = NULL;
+ HASH_ITER(hh, src->hash_table, node, tmp) {
+ cube_manager_add(pthis, cube_fork(node));
+ }
+ return pthis;
+}
+
+int cube_manager_merge(struct cube_manager *dest, const struct cube_manager *src)
+{
+ struct cube *node = NULL;
+ struct cube *tmp = NULL;
+ int ret = FS_OK;
+ HASH_ITER(hh, src->hash_table, node, tmp) {
+ struct cube *node_in_dest = NULL;
+ HASH_FIND(hh, dest->hash_table, node->serialized_dimensions, node->serialized_dimensions_len, node_in_dest);
+
+ if (node_in_dest == NULL) {
+ cube_manager_add(dest, cube_copy(node));
+ } else {
+ int tmp_ret = cube_merge(node_in_dest, node);
+ if (tmp_ret != FS_OK) {
+ ret = tmp_ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+void cube_manager_reset(struct cube_manager *pthis)
+{
+ for (int i = 0; i < pthis->next_index; i++) {
+ if (pthis->cube_slots[i] == NULL) {
+ continue;
+ }
+ cube_reset(pthis->cube_slots[i]);
+ }
+}
+
+struct metric *find_metric_in_cell(const struct cell *cell, int metric_id)
+{
+ if (metric_id >= cell->next_index) {
+ return NULL;
+ }
+ return cell->slots[metric_id];
+}
+
+void add_metric_to_cell(struct cell *cell, struct metric *metric, int metric_id)
+{
+ if (metric_id >= cell->slots_number) {
+ cell->slots = realloc(cell->slots, sizeof(struct metric *) * cell->slots_number * 2);
+ memset(cell->slots + cell->slots_number, 0, sizeof(struct metric *) * cell->slots_number);
+ cell->slots_number *= 2;
+ }
+
+ cell->slots[metric_id] = metric;
+
+ if (metric_id >= cell->next_index) {
+ cell->next_index = metric_id + 1;
+ }
+}
+
+struct metric *add_or_find_metric_in_cell(const struct metric_manifest *manifest, struct cell *cell)
+{
+ struct metric *metric = find_metric_in_cell(cell, manifest->id);
+ if (metric != NULL) {
+ return metric;
+ }
+
+ metric = metric_new(manifest);
+ add_metric_to_cell(cell, metric, manifest->id);
+ return metric;
+}
+
+struct cell *cell_new(const struct exdata_new_args *args) {
+ struct cell *pthis = malloc(sizeof(struct cell));
+ pthis->slots = calloc(DEFAULT_N_METRIC, sizeof(struct metric *));
+ pthis->slots_number = DEFAULT_N_METRIC;
+ pthis->next_index = 0;
+
+ pthis->cell_dimensions.n_field = args->n_dimensions;
+ pthis->cell_dimensions.field = field_array_duplicate(args->cell_dimensions, args->n_dimensions);
+ return pthis;
+}
+
+void cell_free(struct cell *pthis) {
+ for (size_t i = 0; i < pthis->next_index; i++) {
+ metric_free(pthis->slots[i]);
+ }
+ free(pthis->slots);
+ for (size_t i = 0; i < pthis->cell_dimensions.n_field; i++) {
+ free((char *)pthis->cell_dimensions.field[i].key);
+ if (pthis->cell_dimensions.field[i].type == FIELD_VALUE_CSTRING) {
+ free((char *)pthis->cell_dimensions.field[i].value_str);
+ }
+ }
+ free(pthis->cell_dimensions.field);
+ free(pthis);
+}
+
+struct cell *cell_copy(const struct cell *src) {
+ struct cell *pthis = malloc(sizeof(struct cell));
+ pthis->slots = calloc(src->slots_number, sizeof(struct metric *));
+ pthis->slots_number = src->slots_number;
+ pthis->next_index = src->next_index;
+ for (size_t i = 0; i < src->next_index; i++) {
+ if (src->slots[i] == NULL) {
+ continue;
+ }
+
+ pthis->slots[i] = metric_copy(src->slots[i]);
+ }
+
+ pthis->cell_dimensions.n_field = src->cell_dimensions.n_field;
+ pthis->cell_dimensions.field = field_array_duplicate(src->cell_dimensions.field, src->cell_dimensions.n_field);
+
+ return pthis;
+}
+
+void cell_reset(struct cell *pthis) {
+ for (size_t i = 0; i < pthis->next_index; i++) {
+ if (pthis->slots[i] == NULL) {
+ continue;
+ }
+ metric_reset(pthis->slots[i]);
+ }
+}
+
+void cell_merge(struct cell *dest, const struct cell *src) {
+ for (size_t i = 0; i < src->next_index; i++) {
+ const struct metric *metric_src = src->slots[i];
+ if (metric_src == NULL) {
+ continue;
+ }
+ struct metric *metric_dst = find_metric_in_cell(dest, i);
+
+ if (metric_dst == NULL) {
+ metric_dst = metric_copy(metric_src);
+ add_metric_to_cell(dest, metric_dst, i);
+ } else {
+ metric_merge(metric_dst, metric_src);
+ }
+ }
+}
+
+void *exdata_new_i(void *arg) {
+ return cell_new((struct exdata_new_args *)arg);
+}
+
+void exdata_free_i(void *exdata) {
+ cell_free((struct cell *)exdata);
+}
+
+void exdata_reset_i(void *exdata) {
+ cell_reset((struct cell *)exdata);
+}
+
+void exdata_merge_i(void *dest, void *src) {
+ cell_merge((struct cell *)dest, (struct cell *)src);
+}
+
+void *exdata_copy_i(void *exdata) {
+ return cell_copy((struct cell *)exdata);
+}
+
+struct cube *cube_info_new(const struct field *dimensions, size_t n_dimensions)
+{
+ struct cube *cube = calloc(1, sizeof(struct cube));
+
+ if (n_dimensions == 0) {
+ cube->cube_dimensions = NULL;
+ } else {
+ cube->cube_dimensions = field_array_duplicate(dimensions, n_dimensions);
+ }
+
+ cube->n_dimensions = n_dimensions;
+ field_array_to_key(dimensions, n_dimensions, &cube->serialized_dimensions, &cube->serialized_dimensions_len);
+
+ cube->id = -1;
+ cube->primary_metric_id = -1;
+
+ return cube;
+}
+
+struct cube *cube_new(const struct field *dimensions, size_t n_dimensions)
+{
+ struct cube *cube = cube_info_new(dimensions, n_dimensions);
+ cube->manifest_manager = metric_manifest_manager_new();
+
+ return cube;
+}
+
+int cube_set_sampling(struct cube *cube, enum sampling_mode mode, int max_n_cell, int primary_metric_id) {
+ if (cube->sampling_mode == mode && cube->max_n_cell == max_n_cell && cube->primary_metric_id == primary_metric_id) {
+ return FS_OK;
+ }
+ const struct metric_manifest *manifest = metric_manifest_manager_get_by_id(cube->manifest_manager, primary_metric_id);
+ if (manifest == NULL && mode != SAMPLING_MODE_COMPREHENSIVE) {
+ return FS_ERR_INVALID_METRIC_ID;
+ }
+ if ((mode == SAMPLING_MODE_TOPK && manifest->type != METRIC_TYPE_COUNTER) ||
+ (mode == SAMPLING_MODE_TOP_CARDINALITY && manifest->type != METRIC_TYPE_HLL)) {
+ return FS_ERR_INVALID_METRIC_TYPE;
+ }
+
+ if (cube->primary_metric_id != -1) {
+ // delete previous settings
+ switch (cube->sampling_mode)
+ {
+ case SAMPLING_MODE_TOPK:
+ heavy_keeper_free(cube->heavykeeper);
+ break;
+ case SAMPLING_MODE_COMPREHENSIVE:
+ hash_table_free(cube->table);
+ break;
+ case SAMPLING_MODE_TOP_CARDINALITY:
+ spread_sketch_free(cube->spread_sketch);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ switch (mode)
+ {
+ case SAMPLING_MODE_TOPK:
+ cube->heavykeeper = heavy_keeper_new(max_n_cell);
+ heavy_keeper_set_exdata_schema(cube->heavykeeper, exdata_new_i, exdata_free_i, exdata_merge_i, exdata_reset_i, exdata_copy_i);
+ break;
+ case SAMPLING_MODE_COMPREHENSIVE:
+ cube->table = hash_table_new(max_n_cell);
+ hash_table_set_exdata_schema(cube->table, exdata_new_i, exdata_free_i, exdata_merge_i, exdata_reset_i, exdata_copy_i);
+ break;
+ case SAMPLING_MODE_TOP_CARDINALITY: {
+ int width, depth;
+ unsigned char precision;
+ spread_sketch_get_parameter_recommendation(max_n_cell, &depth, &width, &precision);
+ cube->spread_sketch = spread_sketch_new(depth, width, precision, 0, DUMMY_TIME_VAL);
+ spread_sketch_set_exdata_schema(cube->spread_sketch, exdata_new_i, exdata_free_i, exdata_merge_i, exdata_reset_i, exdata_copy_i);
+ break; }
+ default:
+ assert(0);
+ break;
+ }
+
+ cube->sampling_mode = mode;
+ cube->max_n_cell = max_n_cell;
+ cube->primary_metric_id = primary_metric_id;
+
+ return FS_OK;
+}
+
+void cube_free(struct cube *cube) {
+ switch (cube->sampling_mode)
+ {
+ case SAMPLING_MODE_TOPK:
+ heavy_keeper_free(cube->heavykeeper);
+ break;
+ case SAMPLING_MODE_COMPREHENSIVE:
+ hash_table_free(cube->table);
+ break;
+ case SAMPLING_MODE_TOP_CARDINALITY:
+ spread_sketch_free(cube->spread_sketch);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ for (size_t i = 0; i < cube->n_dimensions; i++) {
+ struct field *field = &cube->cube_dimensions[i];
+ free((char *)field->key);
+ if (field->type == FIELD_VALUE_CSTRING) {
+ free((char *)field->value_str);
+ }
+ }
+ free(cube->cube_dimensions);
+
+ free(cube->serialized_dimensions);
+ metric_manifest_manager_free(cube->manifest_manager);
+
+ free(cube);
+}
+
+void cube_reset(struct cube *cube) {
+ switch (cube->sampling_mode)
+ {
+ case SAMPLING_MODE_TOPK:
+ heavy_keeper_reset(cube->heavykeeper);
+ break;
+ case SAMPLING_MODE_COMPREHENSIVE:
+ hash_table_reset(cube->table);
+ break;
+ case SAMPLING_MODE_TOP_CARDINALITY:
+ spread_sketch_reset(cube->spread_sketch);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+struct cell *get_cell_in_cube_generic(struct cube *cube, const struct field *dimensions, size_t n_dimensions) {
+ char *compound_dimension;
+ size_t compound_dimension_len;
+ field_array_to_key(dimensions, n_dimensions, &compound_dimension, &compound_dimension_len);
+
+ struct exdata_new_args args;
+ args.cell_dimensions = dimensions;
+ args.n_dimensions = n_dimensions;
+
+ struct cell *cell_data = NULL;
+ int tmp_ret;
+ switch (cube->sampling_mode)
+ {
+ case SAMPLING_MODE_COMPREHENSIVE: {
+ cell_data = hash_table_get0_exdata(cube->table, compound_dimension, compound_dimension_len);
+ if (cell_data == NULL) {
+ tmp_ret = hash_table_add(cube->table, compound_dimension, compound_dimension_len, (void *)&args);
+ if (tmp_ret == 1) {
+ cell_data = hash_table_get0_exdata(cube->table, compound_dimension, compound_dimension_len);
+ }
+ }
+ break;}
+ case SAMPLING_MODE_TOPK: {
+ cell_data = heavy_keeper_get0_exdata(cube->heavykeeper, compound_dimension, compound_dimension_len);
+ if (cell_data == NULL) {
+ tmp_ret = heavy_keeper_add(cube->heavykeeper, compound_dimension, compound_dimension_len, 0, (void *)&args);
+ if (tmp_ret == 1) {
+ cell_data = heavy_keeper_get0_exdata(cube->heavykeeper, compound_dimension, compound_dimension_len);
+ }
+ }
+ break;}
+ case SAMPLING_MODE_TOP_CARDINALITY: {
+ cell_data = spread_sketch_get0_exdata(cube->spread_sketch, compound_dimension, compound_dimension_len);
+ if (cell_data == NULL) {
+ tmp_ret = spread_sketch_add_hash(cube->spread_sketch, compound_dimension, compound_dimension_len, DUMMY_ITEM_HASH, (void *)&args, DUMMY_TIME_VAL);
+ if (tmp_ret == 1) {
+ cell_data = spread_sketch_get0_exdata(cube->spread_sketch, compound_dimension, compound_dimension_len);
+ }
+ }
+ break;}
+ default:
+ assert(0);
+ break;
+ }
+
+ free(compound_dimension);
+ return cell_data;
+}
+
+union metric_parameter *construct_parameters(enum metric_type type, ...)
+{
+ union metric_parameter *paras = (union metric_parameter *)malloc(sizeof(union metric_parameter));
+ va_list ap;
+ va_start(ap, type);
+ switch (type) {
+ case METRIC_TYPE_COUNTER:
+ break;
+ case METRIC_TYPE_HLL:
+ paras->hll.precision = (char)va_arg(ap, int);
+ break;
+ case METRIC_TYPE_HISTOGRAM:
+ paras->hdr.lowest_trackable_value = va_arg(ap, long long);
+ paras->hdr.highest_trackable_value = va_arg(ap, long long);
+ paras->hdr.significant_figures = va_arg(ap, int);
+ break;
+ default:
+ assert(0);
+ }
+ va_end(ap);
+ return paras;
+}
+
+int cube_register_counter(struct cube *cube, const char *metric_name) {
+ struct metric_manifest *metric = malloc(sizeof(struct metric_manifest));
+ metric->name = strdup(metric_name);
+ metric->parameters = construct_parameters(METRIC_TYPE_COUNTER);
+ metric->type = METRIC_TYPE_COUNTER;
+
+ int id = metric_manifest_manager_add(cube->manifest_manager, metric);
+ if (id < 0) {
+ free(metric->name);
+ free(metric->parameters);
+ free(metric);
+ return FS_ERR_METRIC_NAME_ALREADY_EXISTS;
+ }
+
+ metric->id = id;
+ return id;
+}
+
+int cube_register_hll(struct cube *cube,const char *metric_name, unsigned char precision) {
+ if (precision < 4 || precision > 18) {
+ return FS_ERR_INVALID_PARAM;
+ }
+
+ struct metric_manifest *metric = malloc(sizeof(struct metric_manifest));
+ metric->name = strdup(metric_name);
+ metric->parameters = construct_parameters(METRIC_TYPE_HLL, precision);
+ metric->type = METRIC_TYPE_HLL;
+
+ int id = metric_manifest_manager_add(cube->manifest_manager, metric);
+ if (id < 0) {
+ free(metric->name);
+ free(metric->parameters);
+ free(metric);
+ return FS_ERR_METRIC_NAME_ALREADY_EXISTS;
+ }
+
+ metric->id = id;
+ return id;
+}
+
+int cube_register_hist(struct cube *cube,const char *metric_name, long long lowest_trackable_value, long long highest_trackable_value, int significant_figures) {
+ // refer to hdr_histogram.h for the rules of parameters. Just copy them here
+ if (lowest_trackable_value < 1) {
+ return FS_ERR_INVALID_PARAM;
+ }
+ if (significant_figures < 1 || significant_figures > 5) {
+ return FS_ERR_INVALID_PARAM;
+ }
+ if (lowest_trackable_value * 2 > highest_trackable_value)
+ {
+ return FS_ERR_INVALID_PARAM;
+ }
+
+ struct metric_manifest *metric = malloc(sizeof(struct metric_manifest));
+ metric->name = strdup(metric_name);
+ metric->parameters = construct_parameters(METRIC_TYPE_HISTOGRAM, lowest_trackable_value, highest_trackable_value, significant_figures);
+ metric->type = METRIC_TYPE_HISTOGRAM;
+
+ int id = metric_manifest_manager_add(cube->manifest_manager, metric);
+ if (id < 0) {
+ free(metric->name);
+ free(metric->parameters);
+ free(metric);
+ return FS_ERR_METRIC_NAME_ALREADY_EXISTS;
+ }
+ metric->id = id;
+
+ return id;
+}
+
+int cube_histogram_record(struct cube *cube, int metric_id, const struct field *dimensions, size_t n_dimensions, long long value) {
+ if (cube->primary_metric_id == -1) {
+ return FS_ERR_CUBE_SAMPLING_NOT_INITIALIZED;
+ }
+ assert(cube->sampling_mode == SAMPLING_MODE_COMPREHENSIVE || (cube->primary_metric_id != metric_id));
+
+ const struct metric_manifest *manifest = metric_manifest_manager_get_by_id(cube->manifest_manager, metric_id);
+ if (manifest == NULL || manifest->type != METRIC_TYPE_HISTOGRAM) {
+ return FS_ERR_INVALID_METRIC_ID;
+ }
+
+ struct cell *cell_data = get_cell_in_cube_generic(cube, dimensions, n_dimensions);
+ if (cell_data == NULL) {
+ return FS_ERR_TOO_MANY_CELLS;
+ }
+
+ struct metric *metric = add_or_find_metric_in_cell(manifest, cell_data);
+
+ int ret = metric_histogram_record(metric, value);
+ if (ret < 0) {
+ return FS_ERR_INVALID_PARAM;
+ }
+ return FS_OK;
+}
+
+int cube_hll_add(struct cube *cube, int metric_id, const struct field *dimensions, size_t n_dimensions, const char *key, size_t key_len) {
+ if (cube->primary_metric_id == -1) {
+ return FS_ERR_CUBE_SAMPLING_NOT_INITIALIZED;
+ }
+
+ assert(cube->sampling_mode != SAMPLING_MODE_TOPK || cube->primary_metric_id != metric_id);
+
+ const struct metric_manifest *manifest = metric_manifest_manager_get_by_id(cube->manifest_manager, metric_id);
+ if (manifest == NULL || manifest->type != METRIC_TYPE_HLL) {
+ return FS_ERR_INVALID_METRIC_ID;
+ }
+
+ if (cube->sampling_mode == SAMPLING_MODE_TOP_CARDINALITY && cube->primary_metric_id == metric_id) {
+ char *compound_dimension;
+ size_t compound_dimension_len;
+ field_array_to_key(dimensions, n_dimensions, &compound_dimension, &compound_dimension_len);
+
+ struct exdata_new_args args;
+ args.cell_dimensions = dimensions;
+ args.n_dimensions = n_dimensions;
+
+ int tmp_ret = spread_sketch_add(cube->spread_sketch, compound_dimension, compound_dimension_len, key, key_len, (void *)&args, DUMMY_TIME_VAL);
+ free(compound_dimension);
+ return tmp_ret == 1 ? FS_OK : FS_ERR_TOO_MANY_CELLS;
+ }
+
+ struct cell *cell_data = get_cell_in_cube_generic(cube, dimensions, n_dimensions);
+ if (cell_data == NULL) {
+ return FS_ERR_TOO_MANY_CELLS;
+ }
+
+ struct metric *metric = add_or_find_metric_in_cell(manifest, cell_data);
+ metric_hll_add(metric, key, key_len);
+ return FS_OK;
+}
+
+uint64_t field_array_to_hash(const struct field *field, size_t n_dimensions) {
+ XXH3_state_t state = {0};
+ XXH3_64bits_reset(&state);
+
+ for (int i = 0; i < n_dimensions; i++) {
+ XXH3_64bits_update(&state, field[i].key, strlen(field[i].key));
+ if (field[i].type != FIELD_VALUE_CSTRING) {
+ XXH3_64bits_update(&state, &field[i].value_longlong, sizeof(long long));
+ } else {
+ XXH3_64bits_update(&state, field[i].value_str, strlen(field[i].value_str));
+ }
+ }
+
+ return XXH3_64bits_digest(&state);
+}
+
+int cube_hll_add_field(struct cube *cube, int metric_id, const struct field *dimensions, size_t n_dimensions, const struct field *item_fields, size_t n_item)
+{
+ if (cube->primary_metric_id == -1) {
+ return FS_ERR_CUBE_SAMPLING_NOT_INITIALIZED;
+ }
+ assert(cube->sampling_mode != SAMPLING_MODE_TOPK || (cube->primary_metric_id != metric_id));
+ const struct metric_manifest *manifest = metric_manifest_manager_get_by_id(cube->manifest_manager, metric_id);
+ if (manifest == NULL || manifest->type != METRIC_TYPE_HLL) {
+ return FS_ERR_INVALID_METRIC_ID;
+ }
+
+ if (cube->sampling_mode == SAMPLING_MODE_TOP_CARDINALITY && cube->primary_metric_id == metric_id) {
+ char *compound_dimension;
+ size_t compound_dimension_len;
+ field_array_to_key(dimensions, n_dimensions, &compound_dimension, &compound_dimension_len);
+
+ struct exdata_new_args args;
+ args.cell_dimensions = dimensions;
+ args.n_dimensions = n_dimensions;
+
+ uint64_t hash = field_array_to_hash(item_fields, n_item);
+
+ int tmp_ret = spread_sketch_add_hash(cube->spread_sketch, compound_dimension, compound_dimension_len, hash, (void *)&args, DUMMY_TIME_VAL);
+ free(compound_dimension);
+ return tmp_ret == 1 ? FS_OK : FS_ERR_TOO_MANY_CELLS;
+ }
+
+ struct cell *cell_data = get_cell_in_cube_generic(cube, dimensions, n_dimensions);
+ if (cell_data == NULL) {
+ return FS_ERR_TOO_MANY_CELLS;
+ }
+ struct metric *metric = add_or_find_metric_in_cell(manifest, cell_data);
+
+ uint64_t hash = field_array_to_hash(item_fields, n_item);
+ metric_hll_add_hash(metric, hash);
+ return FS_OK;
+}
+
+int cube_counter_incrby(struct cube *cube, int metric_id, const struct field *dimensions, size_t n_dimensions, long long increment) {
+ if (cube->primary_metric_id == -1) {
+ return FS_ERR_CUBE_SAMPLING_NOT_INITIALIZED;
+ }
+ if (cube->sampling_mode == SAMPLING_MODE_TOPK && cube->primary_metric_id == metric_id && increment < 0) {
+ return FS_ERR_OPERATION_NOT_SUPPORTED_FOR_PRIMARY_METRIC;
+ }
+ assert(cube->sampling_mode == SAMPLING_MODE_COMPREHENSIVE ||
+ (cube->sampling_mode == SAMPLING_MODE_TOPK) ||
+ (cube->sampling_mode == SAMPLING_MODE_TOP_CARDINALITY && cube->primary_metric_id != metric_id)
+ );
+
+ const struct metric_manifest *manifest = metric_manifest_manager_get_by_id(cube->manifest_manager, metric_id);
+ if (manifest == NULL || manifest->type != METRIC_TYPE_COUNTER) {
+ return FS_ERR_INVALID_METRIC_ID;
+ }
+
+ if (cube->primary_metric_id == metric_id && cube->sampling_mode == SAMPLING_MODE_TOPK) {
+ if (increment < 0) {
+ return FS_ERR_INVALID_PARAM;
+ }
+
+ char *compound_dimension;
+ size_t compound_dimension_len;
+ field_array_to_key(dimensions, n_dimensions, &compound_dimension, &compound_dimension_len);
+
+ struct exdata_new_args args;
+ args.cell_dimensions = dimensions;
+ args.n_dimensions = n_dimensions;
+
+ int tmp_ret = heavy_keeper_add(cube->heavykeeper, compound_dimension, compound_dimension_len, increment, (void *)&args);
+ free(compound_dimension);
+ return tmp_ret == 1 ? FS_OK : FS_ERR_TOO_MANY_CELLS;
+ }
+
+ struct cell *cell_data = get_cell_in_cube_generic(cube, dimensions, n_dimensions);
+ if (cell_data == NULL) {
+ return FS_ERR_TOO_MANY_CELLS;
+ }
+
+ struct metric *metric = add_or_find_metric_in_cell(manifest, cell_data);
+
+ metric_counter_incrby(metric, increment);
+ return FS_OK;
+}
+
+int cube_counter_set(struct cube *cube, int metric_id, const struct field *dimensions, size_t n_dimensions, long long value) {
+ if (cube->primary_metric_id == -1) {
+ return FS_ERR_CUBE_SAMPLING_NOT_INITIALIZED;
+ }
+ if (cube->sampling_mode == SAMPLING_MODE_TOPK && cube->primary_metric_id == metric_id) {
+ return FS_ERR_OPERATION_NOT_SUPPORTED_FOR_PRIMARY_METRIC;
+ }
+
+ const struct metric_manifest *manifest = metric_manifest_manager_get_by_id(cube->manifest_manager, metric_id);
+ if (manifest == NULL || manifest->type != METRIC_TYPE_COUNTER) {
+ return FS_ERR_INVALID_METRIC_ID;
+ }
+
+ struct cell *cell_data = get_cell_in_cube_generic(cube, dimensions, n_dimensions);
+ if (cell_data == NULL) {
+ return FS_ERR_TOO_MANY_CELLS;
+ }
+
+ struct metric *metric = add_or_find_metric_in_cell(manifest, cell_data);
+ metric_counter_set(metric, value);
+ return FS_OK;
+}
+
+struct cube *cube_copy(const struct cube *cube)
+{
+ struct cube *cube_dup = cube_info_new(cube->cube_dimensions, cube->n_dimensions);
+ cube_dup->primary_metric_id = cube->primary_metric_id;
+ cube_dup->sampling_mode = cube->sampling_mode;
+ cube_dup->max_n_cell = cube->max_n_cell;
+
+ switch (cube->sampling_mode)
+ {
+ case SAMPLING_MODE_TOPK:
+ cube_dup->heavykeeper = heavy_keeper_copy(cube->heavykeeper);
+ break;
+ case SAMPLING_MODE_COMPREHENSIVE:
+ cube_dup->table = hash_table_copy(cube->table);
+ break;
+ case SAMPLING_MODE_TOP_CARDINALITY:
+ cube_dup->spread_sketch = spread_sketch_copy(cube->spread_sketch);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ cube_dup->manifest_manager = metric_manifest_manager_copy(cube->manifest_manager);
+
+ return cube_dup;
+}
+
+int cube_merge(struct cube *dest, const struct cube *src)
+{
+ if (dest->sampling_mode != src->sampling_mode) {
+ return FS_ERR_DIFFERENT_CONFIGURATION_FOR_SAME_CUBE;
+ }
+ if (dest->primary_metric_id == -1 || src->primary_metric_id == -1) {
+ return FS_ERR_CUBE_SAMPLING_NOT_INITIALIZED;
+ }
+ if (dest->primary_metric_id != src->primary_metric_id) {
+ return FS_ERR_DIFFERENT_CONFIGURATION_FOR_SAME_CUBE;
+ }
+ size_t n_metric_src = 0;
+ const struct metric_manifest **list_src = metric_manifest_manager_list(src->manifest_manager, &n_metric_src);
+ size_t n_metric_dst = 0;
+ const struct metric_manifest **list_dst = metric_manifest_manager_list(dest->manifest_manager, &n_metric_dst);
+ int len_min = n_metric_src < n_metric_dst ? n_metric_src : n_metric_dst;
+ for (int i = 0; i < len_min; i++) {
+ if (list_src[i]->type != list_dst[i]->type) {
+ return FS_ERR_DIFFERENT_CONFIGURATION_FOR_SAME_CUBE;
+ }
+ if (strcmp(list_src[i]->name, list_dst[i]->name) != 0) {
+ return FS_ERR_DIFFERENT_CONFIGURATION_FOR_SAME_CUBE;
+ }
+ }
+
+ for (int i = n_metric_dst; i < n_metric_src; i++) {
+ metric_manifest_manager_add(dest->manifest_manager, metric_manifest_copy(list_src[i]));
+ }
+
+ switch (dest->sampling_mode)
+ {
+ case SAMPLING_MODE_TOPK:
+ heavy_keeper_merge(dest->heavykeeper, src->heavykeeper);
+ break;
+ case SAMPLING_MODE_COMPREHENSIVE:
+ hash_table_merge(dest->table, src->table);
+ break;
+ case SAMPLING_MODE_TOP_CARDINALITY:
+ spread_sketch_merge(dest->spread_sketch, src->spread_sketch);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return FS_OK;
+}
+
+struct cube *cube_fork(const struct cube *cube) {
+ struct cube *ret = cube_info_new(cube->cube_dimensions, cube->n_dimensions);
+ ret->primary_metric_id = cube->primary_metric_id;
+ ret->sampling_mode = cube->sampling_mode;
+ ret->max_n_cell = cube->max_n_cell;
+ ret->manifest_manager = metric_manifest_manager_copy(cube->manifest_manager);
+
+ switch (cube->sampling_mode) {
+ case SAMPLING_MODE_TOPK:
+ ret->heavykeeper = heavy_keeper_new(cube->max_n_cell);
+ heavy_keeper_set_exdata_schema(ret->heavykeeper, exdata_new_i, exdata_free_i, exdata_merge_i, exdata_reset_i, exdata_copy_i);
+ break;
+ case SAMPLING_MODE_COMPREHENSIVE:
+ ret->table = hash_table_new(cube->max_n_cell);
+ hash_table_set_exdata_schema(ret->table, exdata_new_i, exdata_free_i, exdata_merge_i, exdata_reset_i, exdata_copy_i);
+ break;
+ case SAMPLING_MODE_TOP_CARDINALITY: {
+ int width, depth, dummy_time;
+ unsigned char precision;
+ spread_sketch_get_parameter(cube->spread_sketch, &depth, &width, &precision, &dummy_time);
+ ret->spread_sketch = spread_sketch_new(depth, width, precision, 0, DUMMY_TIME_VAL);
+ spread_sketch_set_exdata_schema(ret->spread_sketch, exdata_new_i, exdata_free_i, exdata_merge_i, exdata_reset_i, exdata_copy_i);
+ break;}
+ default:
+ assert(0);
+ break;
+ }
+ return ret;
+}
+
+/* -------------------------------------------------------------------------- */
+/* query */
+/* -------------------------------------------------------------------------- */
+
+struct tmp_sorted_data_spread_sketch_cell {
+ double hll_value;
+ struct cell *data;
+};
+static int compare_tmp_sorted_data_spread_sketch_cell(const void *a, const void *b) { // sort in descending order
+ const struct tmp_sorted_data_spread_sketch_cell *aa = (const struct tmp_sorted_data_spread_sketch_cell *)a;
+ const struct tmp_sorted_data_spread_sketch_cell *bb = (const struct tmp_sorted_data_spread_sketch_cell *)b;
+ if (aa->hll_value < bb->hll_value) {
+ return 1;
+ } else if (aa->hll_value > bb->hll_value) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+void cube_get_cells(const struct cube *cube, struct field_list **cell_dimensions, size_t *n_cell)
+{
+ size_t n_cell_tmp = cube_get_cell_count(cube);
+ if (n_cell_tmp == 0) {
+ *cell_dimensions = NULL;
+ *n_cell = 0;
+ return;
+ }
+
+ char **spread_sketch_keys = NULL;
+ size_t *spread_sketch_keys_lens = NULL;
+ struct cell **cell_datas = (struct cell **)malloc(sizeof(struct cell *) * n_cell_tmp);
+ switch (cube->sampling_mode) {
+ case SAMPLING_MODE_COMPREHENSIVE:
+ hash_table_list(cube->table, (void **)cell_datas, n_cell_tmp);
+ break;
+ case SAMPLING_MODE_TOPK:
+ heavy_keeper_list(cube->heavykeeper, (void **)cell_datas, n_cell_tmp);
+ break;
+ case SAMPLING_MODE_TOP_CARDINALITY: {
+ spread_sketch_list_keys(cube->spread_sketch, &spread_sketch_keys, &spread_sketch_keys_lens, &n_cell_tmp);
+ for (int i = 0; i < n_cell_tmp; i++) {
+ cell_datas[i] = spread_sketch_get0_exdata(cube->spread_sketch, spread_sketch_keys[i], spread_sketch_keys_lens[i]);
+ }
+ }
+ break;
+ default:
+ assert(0);
+ }
+
+ // spread sketch often stores more than max_n_cell. So sort out the top max_n_cell cells.
+ if (cube->sampling_mode == SAMPLING_MODE_TOP_CARDINALITY && n_cell_tmp > cube->max_n_cell) {
+ struct tmp_sorted_data_spread_sketch_cell *tmp_sorted_data = (struct tmp_sorted_data_spread_sketch_cell *)malloc(sizeof(struct tmp_sorted_data_spread_sketch_cell) * n_cell_tmp);
+ for (int i = 0; i < n_cell_tmp; i++) {
+ tmp_sorted_data[i].data = cell_datas[i];
+ tmp_sorted_data[i].hll_value = spread_sketch_get_cardinality(cube->spread_sketch, spread_sketch_keys[i], spread_sketch_keys_lens[i]);
+ }
+ qsort(tmp_sorted_data, n_cell_tmp, sizeof(struct tmp_sorted_data_spread_sketch_cell), compare_tmp_sorted_data_spread_sketch_cell);
+
+ free(cell_datas);
+ cell_datas = (struct cell **)malloc(sizeof(struct cell *) * cube->max_n_cell);
+ for (int i = 0; i < cube->max_n_cell; i++) {
+ cell_datas[i] = tmp_sorted_data[i].data;
+ }
+ n_cell_tmp = cube->max_n_cell;
+ free(tmp_sorted_data);
+ }
+
+ struct field_list *ret = (struct field_list *)malloc(sizeof(struct field_list) * n_cell_tmp);
+ *cell_dimensions = ret;
+ *n_cell = n_cell_tmp;
+ for (int i = 0; i < n_cell_tmp; i++) {
+ struct cell *cell_data = cell_datas[i];
+ struct field_list *field_list_tmp = &ret[i];
+ field_list_tmp->n_field = cell_data->cell_dimensions.n_field;
+ if (field_list_tmp->n_field == 0) {
+ field_list_tmp->field = NULL;
+ continue;
+ }
+ field_list_tmp->field = field_array_duplicate(cell_data->cell_dimensions.field, field_list_tmp->n_field);
+ }
+
+ free(cell_datas);
+ free(spread_sketch_keys);
+ free(spread_sketch_keys_lens);
+}
+
+const struct cell *cube_find_cell_by_dimension(const struct cube *cube, const struct field_list *fields)
+{
+ const struct cell *ret = NULL;
+ char *compound_dimension;
+ size_t dimension_len;
+ field_array_to_key(fields->field, fields->n_field, &compound_dimension, &dimension_len);
+
+ switch (cube->sampling_mode)
+ {
+ case SAMPLING_MODE_TOPK:
+ ret = heavy_keeper_get0_exdata(cube->heavykeeper, compound_dimension, dimension_len);
+ break;
+ case SAMPLING_MODE_COMPREHENSIVE:
+ ret = hash_table_get0_exdata(cube->table, compound_dimension, dimension_len);
+ break;
+ case SAMPLING_MODE_TOP_CARDINALITY:
+ ret = spread_sketch_get0_exdata(cube->spread_sketch, compound_dimension, dimension_len);
+ break;
+ default:
+ assert(0);
+ return NULL;
+ }
+ free(compound_dimension);
+
+ return ret;
+}
+
+const struct metric *cube_find_uncleared_metric_in_cell(const struct cube *cube, const struct field_list *fields, int metric_id,int *ret_code)
+{
+ const struct cell *data = cube_find_cell_by_dimension(cube, fields);
+
+ if (data == NULL) {
+ *ret_code = FS_ERR_INVALID_DIMENSION;
+ return NULL;
+ }
+
+ if (metric_id < 0 || metric_id >= data->next_index) {
+ *ret_code = FS_ERR_INVALID_METRIC_ID;
+ return NULL;
+ }
+ *ret_code = FS_OK;
+
+ const struct metric *ret_metric = data->slots[metric_id];
+ if (ret_metric == NULL || metric_check_if_cleared(ret_metric)) {
+ *ret_code = FS_ERR_INVALID_METRIC_ID;
+ return NULL;
+ }
+
+ return ret_metric;
+}
+
+int cube_counter_get(const struct cube *cube, int metric_id, const struct field_list *fields, long long *value)
+{
+ if (cube->sampling_mode == SAMPLING_MODE_TOPK && cube->primary_metric_id == metric_id) {
+ char *dimension_in_string;
+ size_t dimension_string_len;
+ field_array_to_key(fields->field, fields->n_field, &dimension_in_string, &dimension_string_len);
+
+ long long count = 0;
+ void *exdata_dummy = NULL;
+ heavy_keeper_one_point_query(cube->heavykeeper, dimension_in_string, dimension_string_len, &count, &exdata_dummy);
+ *value = count;
+
+ free(dimension_in_string);
+ return count == 0 ? FS_ERR_INVALID_DIMENSION : FS_OK;
+ }
+
+ int ret;
+ const struct metric *metric = cube_find_uncleared_metric_in_cell(cube, fields, metric_id, &ret);
+ if (ret != FS_OK) {
+ return ret;
+ }
+ if (metric == NULL) {
+ return FS_ERR_INVALID_METRIC_ID;
+ }
+
+ *value = metric_counter_get(metric);
+ return FS_OK;
+}
+
+int cube_hll_get(const struct cube *cube, int metric_id, const struct field_list *fields, double *value)
+{
+ if (cube->sampling_mode == SAMPLING_MODE_TOP_CARDINALITY && cube->primary_metric_id == metric_id) {
+ char *dimension_in_string;
+ size_t dimension_string_len;
+ field_array_to_key(fields->field, fields->n_field, &dimension_in_string, &dimension_string_len);
+
+ double hll_value = spread_sketch_get_cardinality(cube->spread_sketch, dimension_in_string, dimension_string_len);
+ free(dimension_in_string);
+ if (hll_value < 0) {
+ return FS_ERR_INVALID_DIMENSION;
+ }
+
+ *value = hll_value;
+ return FS_OK;
+ }
+
+ int ret;
+ const struct metric *metric = cube_find_uncleared_metric_in_cell(cube, fields, metric_id, &ret);
+ if (ret != FS_OK) {
+ return ret;
+ }
+ if (metric == NULL) {
+ return FS_ERR_INVALID_METRIC_ID;
+ }
+
+ *value = metric_hll_get(metric);
+ return FS_OK;
+}
+
+int cube_histogram_value_at_percentile(const struct cube *cube, int metric_id, const struct field_list *fields, double percentile, long long *value)
+{
+ int ret;
+ const struct metric *metric = cube_find_uncleared_metric_in_cell(cube, fields, metric_id, &ret);
+ if (ret != FS_OK) {
+ return ret;
+ }
+ if (metric == NULL) {
+ return FS_ERR_INVALID_METRIC_ID;
+ }
+
+ *value = metric_histogram_value_at_percentile(metric, percentile);
+ return FS_OK;
+}
+
+int cube_histogram_count_le_value(const struct cube *cube, int metric_id, const struct field_list *fields, long long value, long long *count) {
+ int ret;
+ const struct metric *metric = cube_find_uncleared_metric_in_cell(cube, fields, metric_id, &ret);
+ if (ret != FS_OK) {
+ return ret;
+ }
+ if (metric == NULL) {
+ return FS_ERR_INVALID_METRIC_ID;
+ }
+
+ *count = metric_histogram_count_le_value(metric, value);
+ return FS_OK;
+}
+
+int cube_get_serialization_as_base64(const struct cube *cube, int metric_id, const struct field_list *fields, char **blob, size_t *blob_size) {
+ if (cube->sampling_mode == SAMPLING_MODE_TOP_CARDINALITY && cube->primary_metric_id == metric_id) {
+ char *dimension_in_string;
+ size_t dimension_string_len;
+ field_array_to_key(fields->field, fields->n_field, &dimension_in_string, &dimension_string_len);
+
+ *blob = spread_sketch_get_hll_base64_serialization(cube->spread_sketch, dimension_in_string, dimension_string_len);
+ *blob_size = strlen(*blob);
+ free(dimension_in_string);
+ return FS_OK;
+ }
+
+ int ret;
+ const struct metric *metric = cube_find_uncleared_metric_in_cell(cube, fields, metric_id, &ret);
+ if (ret != FS_OK) {
+ return ret;
+ }
+ if (metric == NULL) {
+ return FS_ERR_INVALID_METRIC_ID;
+ }
+
+ metric_serialize(metric, blob, blob_size);
+ return FS_OK;
+}
+
+int cube_get_cell_count(const struct cube *cube) {
+ switch (cube->sampling_mode) {
+ case SAMPLING_MODE_COMPREHENSIVE:
+ return hash_table_get_count(cube->table);
+ case SAMPLING_MODE_TOPK:
+ return heavy_keeper_get_count(cube->heavykeeper);
+ case SAMPLING_MODE_TOP_CARDINALITY:
+ return spread_sketch_get_count(cube->spread_sketch);
+ default:
+ assert(0);
+ return -1; // to mute cppcheck
+ }
+}
+
+void cube_get_metrics_in_cell(const struct cube *cube, const struct field_list *fields, int **metric_id_out, size_t *n_metric_out) {
+ const struct cell *cell_data = cube_find_cell_by_dimension(cube, fields);
+ if (cell_data == NULL) {
+ *metric_id_out = NULL;
+ *n_metric_out = 0;
+ return;
+ }
+
+ *metric_id_out = (int *)malloc(sizeof(int) * cell_data->next_index + 1); // +1: for primary metric
+ int n_metric = 0;
+ if (cube->sampling_mode == SAMPLING_MODE_TOP_CARDINALITY || cube->sampling_mode == SAMPLING_MODE_TOPK) { // primary metric is not stored in cell_data
+ (*metric_id_out)[n_metric] = cube->primary_metric_id;
+ n_metric++;
+ }
+
+ for (int i = 0; i < cell_data->next_index; i++) {
+ if (cell_data->slots[i] != NULL && !metric_check_if_cleared(cell_data->slots[i])) {
+ (*metric_id_out)[n_metric] = i;
+ n_metric++;
+ }
+ }
+ *n_metric_out = n_metric;
+}
+
+struct field_list *cube_get_identifier(const struct cube *cube) {
+ struct field_list *ret = (struct field_list *)malloc(sizeof(struct field_list));
+
+ if (cube->n_dimensions == 0) {
+ ret->field = NULL;
+ ret->n_field = 0;
+ return ret;
+ }
+
+ ret->field = field_array_duplicate(cube->cube_dimensions, cube->n_dimensions);
+ ret->n_field = cube->n_dimensions;
+
+
+ return ret;
+}
+
+const char *cube_get_metric_name(const struct cube *cube, int metric_id) {
+ const struct metric_manifest *metric = metric_manifest_manager_get_by_id(cube->manifest_manager, metric_id);
+ if (metric == NULL) {
+ return NULL;
+ }
+
+ return metric->name;
+}
+
+enum metric_type cube_get_metric_type(const struct cube *cube, int metric_id) {
+ const struct metric_manifest *metric = metric_manifest_manager_get_by_id(cube->manifest_manager, metric_id);
+ if (metric == NULL) {
+ return (enum metric_type)(-1);
+ }
+
+ return metric->type;
+}
+
+void cube_get_metrics(const struct cube *cube, int **metric_id_out, size_t *n_metric)
+{
+ const struct metric_manifest **list = metric_manifest_manager_list(cube->manifest_manager, n_metric);
+ if (*n_metric == 0) {
+ *metric_id_out = NULL;
+ return;
+ }
+
+ int *tmp_ids = (int *)malloc(sizeof(int) * (*n_metric));
+ *metric_id_out = tmp_ids;
+
+ for (int i = 0; i < *n_metric; i++) {
+ tmp_ids[i] = list[i]->id;
+ }
+}
diff --git a/src/cube.h b/src/cube.h
new file mode 100644
index 0000000..1134aa6
--- /dev/null
+++ b/src/cube.h
@@ -0,0 +1,71 @@
+#pragma once
+#include <stdio.h>
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdbool.h>
+#include "fieldstat.h" // for dimensions
+#include "metric_manifest.h"
+
+struct cube;
+struct cube_manager;
+
+struct cube *cube_new(const struct field *dimensions, size_t n_dimensions);
+int cube_set_sampling(struct cube *cube, enum sampling_mode mode, int max_n_cell, int primary_metric_id);
+void cube_free(struct cube *cube);
+void cube_reset(struct cube *cube);
+struct cube *cube_copy(const struct cube *cube);
+int cube_merge(struct cube *dest, const struct cube *src);
+struct cube *cube_fork(const struct cube *cube); // only copy the cube configurations, leave the cells empty
+
+int cube_register_counter(struct cube *cube, const char *metric_name);
+int cube_register_hll(struct cube *cube,const char *metric_name, unsigned char precision);
+int cube_register_hist(struct cube *cube,const char *metric_name, long long lowest_trackable_value, long long highest_trackable_value, int significant_figures);
+
+int cube_histogram_record(struct cube *cube, int metric_id, const struct field *dimensions, size_t n_dimensions, long long value);
+int cube_hll_add(struct cube *cube, int metric_id, const struct field *dimensions, size_t n_dimensions, const char *key, size_t key_len);
+int cube_hll_add_field(struct cube *cube, int metric_id, const struct field *dimensions, size_t n_dimensions, const struct field *item_fields, size_t n_item);
+int cube_counter_incrby(struct cube *cube, int metric_id, const struct field *dimensions, size_t n_dimensions, long long increment);
+int cube_counter_set(struct cube *cube, int metric_id, const struct field *dimensions, size_t n_dimensions, long long value);
+
+/* ---------------------------------- query --------------------------------- */
+int cube_counter_get(const struct cube *cube, int metric_id, const struct field_list *dimensions, long long *value);
+int cube_hll_get(const struct cube *cube, int metric_id, const struct field_list *dimensions, double *value);
+int cube_histogram_value_at_percentile(const struct cube *cube, int metric_id, const struct field_list *dimensions, double percentile, long long *value);
+int cube_histogram_count_le_value(const struct cube *cube, int metric_id, const struct field_list *dimensions, long long value, long long *count);
+int cube_get_serialization_as_base64(const struct cube *cube, int metric_id, const struct field_list *dimensions, char **blob, size_t *blob_size);
+
+int cube_get_cell_count(const struct cube *cube);
+enum sampling_mode cube_get_sampling_mode(const struct cube *cube);
+void cube_get_cells(const struct cube *cube, struct field_list **cell_dimensions, size_t *n_cell);
+void cube_get_metrics(const struct cube *cube, int **metric_id_out, size_t *n_metric);
+void cube_get_metrics_in_cell(const struct cube *cube, const struct field_list *dimensions, int **metric_id_out, size_t *n_metric_out);
+struct field_list *cube_get_identifier(const struct cube *cube);
+const char *cube_get_metric_name(const struct cube *cube, int metric_id);
+enum metric_type cube_get_metric_type(const struct cube *cube, int metric_id);
+
+/* -------------------------------------------------------------------------- */
+/* cube manager */
+/* -------------------------------------------------------------------------- */
+struct cube *cube_manager_get_cube_by_id(const struct cube_manager *manager, int cube_id);
+
+// the cube will be taken over by the manager, user do not free it.
+int cube_manager_add(struct cube_manager *pthis, struct cube *cube);
+void cube_manager_delete(struct cube_manager *pthis, struct cube *cube); // the cube will be freed by the manager
+int cube_manager_find(const struct cube_manager *pthis, const struct field *cube_dimensions, size_t n_dimensions);
+struct cube_manager *cube_manager_new();
+void cube_manager_free(struct cube_manager *pthis);
+int cube_manager_merge(struct cube_manager *dest, const struct cube_manager *src);
+
+void cube_manager_reset(struct cube_manager *pthis);
+void cube_manager_calibrate(struct cube_manager *pthis, const struct cube_manager *master);
+struct cube_manager *cube_manager_fork(const struct cube_manager *src);
+
+void cube_manager_list(const struct cube_manager *pthis, int **cube_ids, int *n_cube);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/src/exdata.h b/src/exdata.h
new file mode 100644
index 0000000..a9c28de
--- /dev/null
+++ b/src/exdata.h
@@ -0,0 +1,7 @@
+#pragma once
+
+typedef void * (*exdata_new_cb)(void *arg);
+typedef void (*exdata_free_cb)(void *exdata);
+typedef void (*exdata_merge_cb)(void *dest, void *src);
+typedef void (*exdata_reset_cb)(void *exdata);
+typedef void * (*exdata_copy_cb)(void *exdata);
diff --git a/src/exporter/cjson_exporter.c b/src/exporter/cjson_exporter.c
index 206f7b1..5954a1e 100644
--- a/src/exporter/cjson_exporter.c
+++ b/src/exporter/cjson_exporter.c
@@ -46,11 +46,12 @@
]
*/
#define DEFAULT_EXPORTER_NAME "-"
+#define NIL_TAG_JSON "\anil"
struct counter_history;
struct export_kv_pair {
char *key;
- enum fs_tag_type type;
+ enum field_type type;
union{
long long value_longlong;
double value_double;
@@ -60,7 +61,7 @@ struct export_kv_pair {
struct fieldstat_json_exporter {
char *name;
- struct fieldstat_tag_list *global_tag_list;
+ struct field_list *global_tag_list;
struct counter_history *history;
long long last_ts;
};
@@ -69,20 +70,17 @@ struct cell_iter {
int *cube_ids;
int n_cube;
int curr_cube_idx;
- struct fieldstat_tag_list *shared_tag;
+ struct field_list *cube_dimension;
- struct fieldstat_tag_list *tag_list;
+ struct field_list *cell_dimension;
size_t n_cell;
size_t curr_cell_idx;
- int *metric_ids;
- size_t n_metric;
-
const struct fieldstat *instance;
};
struct cellwise_rec_for_export {
- struct json_writer *cjson_tags;
+ struct json_writer *tag_json_obj;
struct export_kv_pair **metric_pairs;
size_t n_metric;
};
@@ -103,12 +101,8 @@ struct counter_history {
struct tag_metric_map *rec;
long long ts;
- unsigned long cell_version;
- unsigned long *cube_version;
- size_t n_cube;
-
char *exporter_name;
- struct fieldstat_tag_list *global_tag_list;
+ struct field_list *global_tag_list;
};
struct couple_export_table_item {
@@ -123,25 +117,25 @@ struct couple_export_table {
void kv_pair_free(struct export_kv_pair *pair) {
- if (pair->type == TAG_CSTRING) {
+ if (pair->type == FIELD_VALUE_CSTRING) {
free((char *)pair->value_str);
}
free((char *)pair->key);
free(pair);
}
-void kv_pair_fill_with_tags(struct export_kv_pair *dest, const struct fieldstat_tag *src)
+void kv_pair_fill_with_fields(struct export_kv_pair *dest, const struct field *src)
{
dest->key = strdup(src->key);
dest->type = src->type;
switch (src->type) {
- case TAG_INTEGER:
+ case FIELD_VALUE_INTEGER:
dest->value_longlong = src->value_longlong;
break;
- case TAG_DOUBLE:
+ case FIELD_VALUE_DOUBLE:
dest->value_double = src->value_double;
break;
- case TAG_CSTRING:
+ case FIELD_VALUE_CSTRING:
dest->value_str = strdup(src->value_str);
break;
default:
@@ -212,44 +206,42 @@ void counter_history_free(struct counter_history *history)
free(tag_node);
}
- free(history->cube_version);
-
free(history->exporter_name);
if (history->global_tag_list != NULL) {
- fieldstat_tag_list_arr_free(history->global_tag_list, 1);
+ fieldstat_field_list_arr_free(history->global_tag_list, 1);
}
free(history);
}
-bool fieldstat_tag_list_cmp(const struct fieldstat_tag_list *a, const struct fieldstat_tag_list *b)
+bool field_list_cmp(const struct field_list *a, const struct field_list *b)
{
- if (a->n_tag != b->n_tag) {
+ if (a->n_field != b->n_field) {
return false;
}
- for (int i = 0; i < a->n_tag; i++) {
- if (strcmp(a->tag[i].key, b->tag[i].key) != 0) {
+ for (int i = 0; i < a->n_field; i++) {
+ if (strcmp(a->field[i].key, b->field[i].key) != 0) {
return false;
}
- if (a->tag[i].type != b->tag[i].type) {
+ if (a->field[i].type != b->field[i].type) {
return false;
}
- switch (a->tag[i].type)
+ switch (a->field[i].type)
{
- case TAG_INTEGER:
- if (a->tag[i].value_longlong != b->tag[i].value_longlong) {
+ case FIELD_VALUE_INTEGER:
+ if (a->field[i].value_longlong != b->field[i].value_longlong) {
return false;
}
break;
- case TAG_DOUBLE:
- if (a->tag[i].value_double != b->tag[i].value_double) {
+ case FIELD_VALUE_DOUBLE:
+ if (a->field[i].value_double != b->field[i].value_double) {
return false;
}
break;
- case TAG_CSTRING:
- if (strcmp(a->tag[i].value_str, b->tag[i].value_str) != 0) {
+ case FIELD_VALUE_CSTRING:
+ if (strcmp(a->field[i].value_str, b->field[i].value_str) != 0) {
return false;
}
break;
@@ -261,24 +253,24 @@ bool fieldstat_tag_list_cmp(const struct fieldstat_tag_list *a, const struct fie
return true;
}
-struct fieldstat_tag_list *my_copy_fs_tag_list(const struct fieldstat_tag_list *src)
+struct field_list *field_list_dup(const struct field_list *src)
{
- struct fieldstat_tag_list *dest = malloc(sizeof(struct fieldstat_tag_list));
- dest->n_tag = src->n_tag;
- dest->tag = malloc(sizeof(struct fieldstat_tag) * dest->n_tag);
- for (int i = 0; i < dest->n_tag; i++) {
- dest->tag[i].key = strdup(src->tag[i].key);
- dest->tag[i].type = src->tag[i].type;
- switch (src->tag[i].type)
+ struct field_list *dest = malloc(sizeof(struct field_list));
+ dest->n_field = src->n_field;
+ dest->field = malloc(sizeof(struct field) * dest->n_field);
+ for (int i = 0; i < dest->n_field; i++) {
+ dest->field[i].key = strdup(src->field[i].key);
+ dest->field[i].type = src->field[i].type;
+ switch (src->field[i].type)
{
- case TAG_INTEGER:
- dest->tag[i].value_longlong = src->tag[i].value_longlong;
+ case FIELD_VALUE_INTEGER:
+ dest->field[i].value_longlong = src->field[i].value_longlong;
break;
- case TAG_DOUBLE:
- dest->tag[i].value_double = src->tag[i].value_double;
+ case FIELD_VALUE_DOUBLE:
+ dest->field[i].value_double = src->field[i].value_double;
break;
- case TAG_CSTRING:
- dest->tag[i].value_str = strdup(src->tag[i].value_str);
+ case FIELD_VALUE_CSTRING:
+ dest->field[i].value_str = strdup(src->field[i].value_str);
break;
default:
assert(0);
@@ -290,21 +282,16 @@ struct fieldstat_tag_list *my_copy_fs_tag_list(const struct fieldstat_tag_list *
bool counter_history_check_if_need_to_update(const struct fieldstat_json_exporter *exporter, const struct fieldstat *instance)
{
- struct counter_history *history = exporter->history;
+ const struct counter_history *history = exporter->history;
if (history == NULL) {
return false; // delta export disabled
}
- if (history->cube_version == NULL) { // first time
+ if (history->exporter_name == NULL) { // first time
return true;
}
- unsigned long cur_cell_version = fieldstat_get_version(instance);
const char *cur_exporter_name = exporter->name ? exporter->name : DEFAULT_EXPORTER_NAME;
- const struct fieldstat_tag_list *cur_global_tag_list = exporter->global_tag_list;
-
- if (history->cell_version != cur_cell_version) {
- return true;
- }
+ const struct field_list *cur_global_tag_list = exporter->global_tag_list;
if (strcmp(history->exporter_name, cur_exporter_name) != 0) {
return true;
@@ -316,51 +303,24 @@ bool counter_history_check_if_need_to_update(const struct fieldstat_json_exporte
if (history->global_tag_list == NULL && cur_global_tag_list == NULL) {
return false;
}
- // global tag cant be deleted, so no need to check if cur_global_tag_list == NULL && history->global_tag_list != NULL
- if (fieldstat_tag_list_cmp(cur_global_tag_list, history->global_tag_list) == false) {
- return true;
- }
-
- int *cube_ids = NULL;
- int n_cube = 0;
- fieldstat_get_cubes(instance, &cube_ids, &n_cube);
- if (n_cube != history->n_cube) {
+ // global field cant be deleted, so no need to check if cur_global_tag_list == NULL && history->global_tag_list != NULL
+ if (field_list_cmp(cur_global_tag_list, history->global_tag_list) == false) {
return true;
}
- for (int i = 0; i < n_cube; i++) {
- unsigned long cube_version = fieldstat_get_cube_version(instance, cube_ids[i]);
- if (cube_version != history->cube_version[i]) {
- return true;
- }
- }
- free(cube_ids);
return false;
}
void counter_history_fill_version_info(struct counter_history *history, struct fieldstat_json_exporter *exporter, const struct fieldstat *instance)
{
- unsigned long cur_cell_version = fieldstat_get_version(instance);
const char *cur_exporter_name = exporter->name ? exporter->name : DEFAULT_EXPORTER_NAME;
- const struct fieldstat_tag_list *cur_global_tag_list = exporter->global_tag_list;
+ const struct field_list *cur_global_tag_list = exporter->global_tag_list;
free(history->exporter_name);
history->exporter_name = strdup(cur_exporter_name);
if (cur_global_tag_list != NULL) {
- history->global_tag_list = my_copy_fs_tag_list(cur_global_tag_list);
- }
-
- history->cell_version = cur_cell_version;
-
- int *cube_ids = NULL;
- int n_cube = 0;
- fieldstat_get_cubes(instance, &cube_ids, &n_cube);
- history->n_cube = n_cube;
- history->cube_version = malloc(sizeof(unsigned long) * n_cube);
- for (int i = 0; i < n_cube; i++) {
- history->cube_version[i] = fieldstat_get_cube_version(instance, cube_ids[i]);
+ history->global_tag_list = field_list_dup(cur_global_tag_list);
}
- free(cube_ids);
}
void fieldstat_json_exporter_update_history(struct fieldstat_json_exporter *exporter, const struct fieldstat *instance)
@@ -377,13 +337,13 @@ void fieldstat_json_exporter_update_history(struct fieldstat_json_exporter *expo
void write_delta_to_json(struct fieldstat_json_exporter *exporter, struct cellwise_rec_for_export *tag_field_pair, struct json_writer *field_json)
{
- // for every tag_field_pair, get the tag json string
- const char *tag_json = json_writer_unwrap(tag_field_pair->cjson_tags);
+ // for every tag_field_pair, get the field json string
+ const char *tag_json = json_writer_unwrap(tag_field_pair->tag_json_obj);
if (tag_json == NULL) {
- tag_json = "\a\t\a"; // just a dummy string
+ tag_json = NIL_TAG_JSON;
}
for (int j = 0; j < tag_field_pair->n_metric; j++) {
- if (tag_field_pair->metric_pairs[j]->type != TAG_INTEGER) { // only counter type need to write delta
+ if (tag_field_pair->metric_pairs[j]->type != FIELD_VALUE_INTEGER) { // only counter type need to write delta
continue;
}
const char *metric_name = tag_field_pair->metric_pairs[j]->key;
@@ -448,15 +408,13 @@ void couple_export_table_free(struct couple_export_table *tbl)
free(tbl);
}
-#define NIL_TAG_JSON "\anil"
-
-// align delta fields to acc fields by matching tag json string, return an array with length n_acc, each of whose element match the corresponding acc element by both tag and index
+// align delta fields to acc fields by matching field json string, return an array with length n_acc, each of whose element match the corresponding acc element by both field and index
const struct cellwise_rec_for_export **rearrange_metric_delta(const struct cellwise_rec_for_export *acc, size_t n_acc, const struct cellwise_rec_for_export *delta, size_t n_delta)
{
struct couple_export_table *map = couple_export_table_new();
for (int i = 0; i < n_delta; i++) {
- const char *tag_json = json_writer_unwrap(delta[i].cjson_tags);
- if (tag_json == NULL) { // tag might be empty, give it a dummy name
+ const char *tag_json = json_writer_unwrap(delta[i].tag_json_obj);
+ if (tag_json == NULL) { // field might be empty, give it a dummy name
tag_json = NIL_TAG_JSON;
}
couple_export_table_add(map, tag_json, i);
@@ -465,8 +423,8 @@ const struct cellwise_rec_for_export **rearrange_metric_delta(const struct cellw
const struct cellwise_rec_for_export **ret = calloc(n_acc, sizeof(struct cellwise_rec_for_export *));
for (int id_acc = 0; id_acc < n_acc; id_acc++) {
- const char *tag_str_acc = json_writer_unwrap(acc[id_acc].cjson_tags);
- if (tag_str_acc == NULL) { // tag might be empty, give it a dummy name
+ const char *tag_str_acc = json_writer_unwrap(acc[id_acc].tag_json_obj);
+ if (tag_str_acc == NULL) { // field might be empty, give it a dummy name
tag_str_acc = NIL_TAG_JSON;
}
@@ -495,8 +453,8 @@ struct cell_iter *cell_iter_new(const struct fieldstat *instance) {
}
void cell_iter_free_cell_records(struct cell_iter *iter) {
- fieldstat_tag_list_arr_free(iter->tag_list, iter->n_cell);
- iter->tag_list = NULL;
+ fieldstat_field_list_arr_free(iter->cell_dimension, iter->n_cell);
+ iter->cell_dimension = NULL;
iter->n_cell = 0;
iter->curr_cell_idx = 0;
}
@@ -504,11 +462,10 @@ void cell_iter_free_cell_records(struct cell_iter *iter) {
void cell_iter_free(struct cell_iter *iter) {
cell_iter_free_cell_records(iter);
- if (iter->shared_tag != NULL) {
- fieldstat_tag_list_arr_free(iter->shared_tag, 1);
+ if (iter->cube_dimension != NULL) {
+ fieldstat_field_list_arr_free(iter->cube_dimension, 1);
}
free(iter->cube_ids);
- free(iter->metric_ids);
free(iter);
}
@@ -518,20 +475,12 @@ int cell_iter_next_cube(struct cell_iter *iter) {
while (iter->curr_cube_idx < iter->n_cube - 1) {
int cube_id_next = iter->cube_ids[++iter->curr_cube_idx];
- fieldstat_get_metrics_used_by_cube(instance, cube_id_next, &iter->metric_ids, &iter->n_metric);
- if (iter->n_metric == 0) {
- continue;
- }
-
- // get cell info
- fieldstat_get_cells_used_by_cube(instance, cube_id_next, &iter->tag_list, &iter->n_cell);
+ fieldstat_cube_get_cells(instance, cube_id_next, &iter->cell_dimension, &iter->n_cell);
if (iter->n_cell == 0) {
- free(iter->metric_ids);
- iter->metric_ids = NULL;
continue;
}
- iter->shared_tag = fieldstat_get_shared_tags(iter->instance, iter->cube_ids[iter->curr_cube_idx]);
+ iter->cube_dimension = fieldstat_cube_get_dimensions(iter->instance, iter->cube_ids[iter->curr_cube_idx]);
iter->curr_cell_idx = 0;
return 1;
}
@@ -560,10 +509,9 @@ int cell_iter_next(struct cell_iter *iter) {
}
cell_iter_free_cell_records(iter);
- fieldstat_tag_list_arr_free(iter->shared_tag, 1);
- iter->shared_tag = NULL;
- free(iter->metric_ids);
- iter->metric_ids = NULL;
+ fieldstat_field_list_arr_free(iter->cube_dimension, 1);
+ iter->cube_dimension = NULL;
+
if (cell_iter_next_cube(iter)) {
return 1;
}
@@ -575,30 +523,31 @@ int cell_iter_next(struct cell_iter *iter) {
/* -------------------------------------------------------------------------- */
struct export_kv_pair *cell_query_with_iter(const struct cell_iter *iter, int metric_id) {
- enum metric_type type = fieldstat_get_metric_type(iter->instance, metric_id);
+ int cube_id = iter->cube_ids[iter->curr_cube_idx];
+ enum metric_type type = fieldstat_get_metric_type(iter->instance, cube_id, metric_id);
struct export_kv_pair *ret = NULL;
if (type == METRIC_TYPE_COUNTER) {
long long value;
- int tmp_ret = fieldstat_counter_get(iter->instance, iter->cube_ids[iter->curr_cube_idx], metric_id, &iter->tag_list[iter->curr_cell_idx], &value);
+ int tmp_ret = fieldstat_counter_get(iter->instance, cube_id, &iter->cell_dimension[iter->curr_cell_idx], metric_id, &value);
if (tmp_ret < 0) {
return NULL;
}
ret = malloc(sizeof(struct export_kv_pair));
- ret->key = strdup(fieldstat_get_metric_name(iter->instance, metric_id));
- ret->type = TAG_INTEGER;
+ ret->key = strdup(fieldstat_get_metric_name(iter->instance, cube_id, metric_id));
+ ret->type = FIELD_VALUE_INTEGER;
ret->value_longlong = value;
return ret;
}
if (type == METRIC_TYPE_HLL || type == METRIC_TYPE_HISTOGRAM) {
char *value;
size_t len;
- fieldstat_get_serialized_blob(iter->instance, iter->cube_ids[iter->curr_cube_idx], metric_id, &iter->tag_list[iter->curr_cell_idx], &value, &len);
+ fieldstat_metric_get_serialization_as_base64(iter->instance, cube_id, metric_id, &iter->cell_dimension[iter->curr_cell_idx], &value, &len);
if (value == NULL) {
return NULL;
}
ret = malloc(sizeof(struct export_kv_pair));
- ret->key = strdup(fieldstat_get_metric_name(iter->instance, metric_id));
- ret->type = TAG_CSTRING;
+ ret->key = strdup(fieldstat_get_metric_name(iter->instance, cube_id, metric_id));
+ ret->type = FIELD_VALUE_CSTRING;
ret->value_str = value;
return ret;
}
@@ -610,13 +559,13 @@ void kv_pair_write_to_json(const struct export_kv_pair *pairs, struct json_write
{
switch (pairs->type)
{
- case TAG_INTEGER:
+ case FIELD_VALUE_INTEGER:
json_writer_longlong_field(writer, pairs->key, pairs->value_longlong);
break;
- case TAG_DOUBLE:
+ case FIELD_VALUE_DOUBLE:
json_writer_double_field(writer, pairs->key, pairs->value_double);
break;
- case TAG_CSTRING:
+ case FIELD_VALUE_CSTRING:
json_writer_str_field(writer, pairs->key, pairs->value_str, strlen(pairs->value_str));
break;
default:
@@ -624,52 +573,51 @@ void kv_pair_write_to_json(const struct export_kv_pair *pairs, struct json_write
}
}
-void tag_list_append_to_tag_object(const struct fieldstat_tag_list *tag_list, struct json_writer *cjson_tags)
+void field_list_write_to_json(const struct field_list *field_list, struct json_writer *json_obj)
{
- size_t tag_list_len = tag_list->n_tag;
struct export_kv_pair pairs = {0};
- for (int i = 0; i < tag_list_len; i++) {
- if (tag_list->n_tag == 0) {
+ for (int i = 0; i < field_list->n_field; i++) {
+ if (field_list->n_field == 0) {
continue;
}
memset(&pairs, 0, sizeof(struct export_kv_pair));
- pairs.key = (char *)tag_list->tag[i].key;
- pairs.type = tag_list->tag[i].type;
+ pairs.key = (char *)field_list->field[i].key;
+ pairs.type = field_list->field[i].type;
switch (pairs.type)
{
- case TAG_INTEGER:
- pairs.value_longlong = tag_list->tag[i].value_longlong;
+ case FIELD_VALUE_INTEGER:
+ pairs.value_longlong = field_list->field[i].value_longlong;
break;
- case TAG_DOUBLE:
- pairs.value_double = tag_list->tag[i].value_double;
+ case FIELD_VALUE_DOUBLE:
+ pairs.value_double = field_list->field[i].value_double;
break;
- case TAG_CSTRING:
- pairs.value_str = (char *)tag_list->tag[i].value_str;
+ case FIELD_VALUE_CSTRING:
+ pairs.value_str = (char *)field_list->field[i].value_str;
break;
default:
break;
}
- kv_pair_write_to_json(&pairs, cjson_tags);
+ kv_pair_write_to_json(&pairs, json_obj);
}
}
-void cell_iter_read_tag_list(const struct cell_iter *iter, struct export_kv_pair **exported_tags, size_t *len_out)
+void cell_iter_read_dimensions(const struct cell_iter *iter, struct export_kv_pair **exported_dimensions, size_t *n_dimension_out)
{
- struct fieldstat_tag_list *tag_list_cell = &iter->tag_list[iter->curr_cell_idx];
- struct fieldstat_tag_list *tag_arr_shared = iter->shared_tag;
+ struct field_list *field_list_cell = &iter->cell_dimension[iter->curr_cell_idx];
+ struct field_list *field_list_cube = iter->cube_dimension;
- size_t ret_tag_list_len = tag_list_cell->n_tag + tag_arr_shared->n_tag;
+ size_t ret_n_dimension = field_list_cell->n_field + field_list_cube->n_field;
- struct export_kv_pair *tag_list = malloc(sizeof(struct export_kv_pair) * ret_tag_list_len);
- *exported_tags = tag_list;
- *len_out = ret_tag_list_len;
+ struct export_kv_pair *ret = malloc(sizeof(struct export_kv_pair) * ret_n_dimension);
+ *exported_dimensions = ret;
+ *n_dimension_out = ret_n_dimension;
- for (int i = 0; i < tag_list_cell->n_tag; i++) {
- kv_pair_fill_with_tags(tag_list++, &tag_list_cell->tag[i]);
+ for (int i = 0; i < field_list_cell->n_field; i++) {
+ kv_pair_fill_with_fields(ret++, &field_list_cell->field[i]);
}
- for (int i = 0; i < tag_arr_shared->n_tag; i++) {
- kv_pair_fill_with_tags(tag_list++, &tag_arr_shared->tag[i]);
+ for (int i = 0; i < field_list_cube->n_field; i++) {
+ kv_pair_fill_with_fields(ret++, &field_list_cube->field[i]);
}
}
@@ -677,7 +625,7 @@ void kv_pair_free_list(struct export_kv_pair *pairs, size_t len)
{
for (int i = 0; i < len; i++) {
struct export_kv_pair *pair = &pairs[i];
- if (pair->type == TAG_CSTRING) {
+ if (pair->type == FIELD_VALUE_CSTRING) {
free(pair->value_str);
}
free(pair->key);
@@ -686,37 +634,44 @@ void kv_pair_free_list(struct export_kv_pair *pairs, size_t len)
}
// return 1 if added, 0 if not added
-int cjson_map_add(struct cellwise_rec_for_export *tag_field_pair, const struct cell_iter *iter)
+int json_obj_add(struct cellwise_rec_for_export *tag_field_pair, const struct cell_iter *iter)
{
struct export_kv_pair **fields = NULL;
- int n_nonempty_metrics = 0;
+ int *metric_ids = NULL;
+ size_t n_metric = 0;
+ fieldstat_get_metric_in_cell(iter->instance, iter->cube_ids[iter->curr_cube_idx], &iter->cell_dimension[iter->curr_cell_idx], &metric_ids, &n_metric);
+ if (n_metric == 0) {
+ // printf("cannot get metric in cell\n");
+ return 0;
+ }
- for (int i = 0; i < iter->n_metric; i++) {
- struct export_kv_pair *field = cell_query_with_iter(iter, iter->metric_ids[i]);
+ int n_nonempty_metrics = 0;
+ for (int i = 0; i < n_metric; i++) {
+ struct export_kv_pair *field = cell_query_with_iter(iter, metric_ids[i]);
if (field == NULL) {
continue;
}
if (fields == NULL) {
- fields = malloc(sizeof(struct export_kv_pair *) * iter->n_metric);
+ fields = malloc(sizeof(struct export_kv_pair *) * n_metric);
}
fields[n_nonempty_metrics++] = field;
}
-
+ free(metric_ids);
if (fields == NULL) { // all fields are null
return 0;
}
tag_field_pair->metric_pairs = fields;
tag_field_pair->n_metric = n_nonempty_metrics;
- struct export_kv_pair *tag_list;
- size_t tag_list_len;
- cell_iter_read_tag_list(iter, &tag_list, &tag_list_len);
+ struct export_kv_pair *dimensions;
+ size_t n_pair;
+ cell_iter_read_dimensions(iter, &dimensions, &n_pair);
struct json_writer *writer_tag = json_writer_init();
- for (int i = 0; i < tag_list_len; i++) {
- kv_pair_write_to_json(&tag_list[i], writer_tag);
+ for (int i = 0; i < n_pair; i++) {
+ kv_pair_write_to_json(&dimensions[i], writer_tag);
}
- kv_pair_free_list(tag_list, tag_list_len);
- tag_field_pair->cjson_tags = writer_tag;
+ kv_pair_free_list(dimensions, n_pair);
+ tag_field_pair->tag_json_obj = writer_tag;
return 1;
}
@@ -746,7 +701,7 @@ struct cellwise_rec_for_export *read_tag_and_field(const struct fieldstat *insta
int i = 0;
while (cell_iter_next(iter)) {
// next_num++;
- if (cjson_map_add(&tag_field_pair[i], iter) == 0) {
+ if (json_obj_add(&tag_field_pair[i], iter) == 0) {
continue;
}
i++;
@@ -765,7 +720,7 @@ void fieldstat_json_exporter_write_global_tags(const struct fieldstat_json_expor
}
for (int i = 0; i < arr_len; i++) {
- tag_list_append_to_tag_object(exporter->global_tag_list, tag_field_pair_arr[i].cjson_tags);
+ field_list_write_to_json(exporter->global_tag_list, tag_field_pair_arr[i].tag_json_obj);
}
}
@@ -875,7 +830,7 @@ void fieldstat_json_exporter_export_array(const struct fieldstat_json_exporter *
json_writer_start_map(root);
json_writer_str_field(root, "name", tmp_name, strlen(tmp_name));
- json_writer_object_item(root, "tags", current->cjson_tags);
+ json_writer_object_item(root, "tags", current->tag_json_obj);
json_writer_object_item(root, "fields", field_json);
if (exporter->history != NULL) {
json_writer_object_item(root, "fields_delta", field_json_delta);
@@ -919,7 +874,7 @@ char *fieldstat_json_exporter_export(const struct fieldstat_json_exporter *expor
int buf_len = 4096;
char *cjson_str_arr = (char *)malloc(buf_len);
- // cjson is so slow, so we construct the json array manually, the reason why we don't use json_writer is simply because I wrote this before implementing json_writer
+ // cjson is so slow, so we construct the json array manually
int used_len = add_object_to_json_array_start(cjson_str_arr, buf_len);
for (int i = 0; i < n_pair; i++) {
used_len = add_object_to_json_array(&cjson_str_arr, &buf_len, used_len, str_arr[i]);
@@ -970,7 +925,7 @@ char *fieldstat_json_exporter_export_with_delta(const struct fieldstat_json_expo
json_writer_start_map(root);
json_writer_str_field(root, "name", tmp_name, strlen(tmp_name));
- json_writer_object_item(root, "tags", current->cjson_tags);
+ json_writer_object_item(root, "tags", current->tag_json_obj);
json_writer_object_item(root, "fields", field_json);
json_writer_object_item(root, "fields_delta", field_json_delta);
json_writer_longlong_field(root, "timestamp_ms", cal_ms_time(timestamp));
@@ -999,7 +954,7 @@ char *fieldstat_json_exporter_export_with_delta(const struct fieldstat_json_expo
free(expair_delta[i].metric_pairs);
char *dummy_s;
size_t dummy_len;
- json_writer_finish(expair_delta[i].cjson_tags, &dummy_s, &dummy_len); // the tag json is not added to the root json, so we need to free it manually
+ json_writer_finish(expair_delta[i].tag_json_obj, &dummy_s, &dummy_len); // the field json is not added to the root json, so we need to free it manually
free(dummy_s);
}
free(expair_delta);
@@ -1019,30 +974,30 @@ struct fieldstat_json_exporter *fieldstat_json_exporter_new()
return exporter;
}
-void fieldstat_json_exporter_set_global_tag(struct fieldstat_json_exporter *exporter, const struct fieldstat_tag tag_list[], size_t n_tag)
+void fieldstat_json_exporter_set_global_tag(struct fieldstat_json_exporter *exporter, const struct field tag_list[], size_t n_field)
{
if (exporter->global_tag_list != NULL) {
- free(exporter->global_tag_list->tag);
+ free(exporter->global_tag_list->field);
free(exporter->global_tag_list);
}
- exporter->global_tag_list = malloc(sizeof(struct fieldstat_tag_list));
- exporter->global_tag_list->n_tag = n_tag;
- exporter->global_tag_list->tag = malloc(sizeof(struct fieldstat_tag) * n_tag);
- for (size_t i = 0; i < n_tag; i++) {
- struct fieldstat_tag *tag = &exporter->global_tag_list->tag[i];
- tag->key = strdup(tag_list[i].key);
- tag->type = tag_list[i].type;
- switch (tag->type)
+ exporter->global_tag_list = malloc(sizeof(struct field_list));
+ exporter->global_tag_list->n_field = n_field;
+ exporter->global_tag_list->field = malloc(sizeof(struct field) * n_field);
+ for (size_t i = 0; i < n_field; i++) {
+ struct field *field = &exporter->global_tag_list->field[i];
+ field->key = strdup(tag_list[i].key);
+ field->type = tag_list[i].type;
+ switch (field->type)
{
- case TAG_INTEGER:
- tag->value_longlong = tag_list[i].value_longlong;
+ case FIELD_VALUE_INTEGER:
+ field->value_longlong = tag_list[i].value_longlong;
break;
- case TAG_CSTRING:
- tag->value_str = strdup(tag_list[i].value_str);
+ case FIELD_VALUE_CSTRING:
+ field->value_str = strdup(tag_list[i].value_str);
break;
- case TAG_DOUBLE:
- tag->value_double = tag_list[i].value_double;
+ case FIELD_VALUE_DOUBLE:
+ field->value_double = tag_list[i].value_double;
break;
default:
@@ -1065,7 +1020,7 @@ void fieldstat_json_exporter_free(struct fieldstat_json_exporter *exporter)
free(exporter->name);
}
if (exporter->global_tag_list != NULL) {
- fieldstat_tag_list_arr_free(exporter->global_tag_list, 1);
+ fieldstat_field_list_arr_free(exporter->global_tag_list, 1);
}
if (exporter->history != NULL) {
@@ -1082,3 +1037,81 @@ void fieldstat_json_exporter_enable_delta(struct fieldstat_json_exporter *export
}
exporter->history = counter_history_new();
}
+
+void fieldstat_json_exporter_export_flat_array(const struct fieldstat_json_exporter *exporter, const struct fieldstat *instance, const struct timeval *timestamp, char ***output, size_t *output_size) {
+ long long timestamp_ms = cal_ms_time(timestamp);
+
+ size_t n_pair;
+ struct cellwise_rec_for_export *tag_field_pair = read_tag_and_field(instance, &n_pair);
+ if (tag_field_pair == NULL || n_pair == 0) {
+ free(tag_field_pair); // tag_field_pair is not NULL when there are registered metrics but no valid cells
+ *output = NULL;
+ *output_size = 0;
+ return;
+ }
+
+ fieldstat_json_exporter_write_global_tags(exporter, tag_field_pair, n_pair);
+
+ char **cjson_str_arr = (char **)malloc(sizeof(char *) * n_pair);
+ for (int i = 0; i < n_pair; i++) {
+ struct cellwise_rec_for_export *current = &tag_field_pair[i];
+ struct json_writer *root = current->tag_json_obj;
+ for (int j = 0; j < tag_field_pair[i].n_metric; j++) {
+ kv_pair_write_to_json(tag_field_pair[i].metric_pairs[j], root);
+ }
+
+ const char *tmp_name = exporter->name ? exporter->name : DEFAULT_EXPORTER_NAME;
+ json_writer_str_field(root, "name", tmp_name, strlen(tmp_name));
+ json_writer_longlong_field(root, "timestamp_ms", timestamp_ms);
+
+ char *cjson_str;
+ size_t cjson_str_len;
+ json_writer_finish(root, &cjson_str, &cjson_str_len);
+ char *cjson_with_braces = malloc(cjson_str_len + 3);
+ cjson_with_braces[0] = '{';
+ memcpy(cjson_with_braces + 1, cjson_str, cjson_str_len);
+ cjson_with_braces[cjson_str_len + 1] = '}';
+ cjson_with_braces[cjson_str_len + 2] = '\0';
+
+ cjson_str_arr[i] = cjson_with_braces;
+ free(cjson_str);
+ }
+
+ *output = cjson_str_arr;
+ *output_size = n_pair;
+
+ for (int i = 0; i < n_pair; i++) {
+ for (int j = 0; j < tag_field_pair[i].n_metric; j++) {
+ kv_pair_free(tag_field_pair[i].metric_pairs[j]);
+ }
+ free(tag_field_pair[i].metric_pairs);
+ }
+
+ free(tag_field_pair);
+}
+
+char *fieldstat_json_exporter_export_flat(const struct fieldstat_json_exporter *exporter, const struct fieldstat *instance, const struct timeval *timestamp)
+{
+ char **str_arr = NULL;
+ size_t n_pair = 0;
+ fieldstat_json_exporter_export_flat_array(exporter, instance, timestamp, &str_arr, &n_pair);
+ if (str_arr == NULL || n_pair == 0) {
+ return NULL;
+ }
+
+ int buf_len = 4096;
+ char *cjson_str_arr = (char *)malloc(buf_len);
+ // cjson is so slow, so we construct the json array manually
+ int used_len = add_object_to_json_array_start(cjson_str_arr, buf_len);
+ for (int i = 0; i < n_pair; i++) {
+ used_len = add_object_to_json_array(&cjson_str_arr, &buf_len, used_len, str_arr[i]);
+ }
+ add_object_to_json_array_end(&cjson_str_arr, buf_len, used_len);
+
+ for (int i = 0; i < n_pair; i++) {
+ free(str_arr[i]);
+ }
+ free(str_arr);
+
+ return cjson_str_arr;
+} \ No newline at end of file
diff --git a/src/exporter/fieldstat_exporter.py b/src/exporter/fieldstat_exporter.py
index 49cb30f..d4e5b2c 100644
--- a/src/exporter/fieldstat_exporter.py
+++ b/src/exporter/fieldstat_exporter.py
@@ -25,11 +25,11 @@ class FieldstatAPI:
libfieldstat.fieldstat_histogram_free.argtypes = [ctypes.c_void_p]
- libfieldstat.fieldstat_histogram_value_at_percentile.argtypes = [ctypes.c_void_p, ctypes.c_double]
- libfieldstat.fieldstat_histogram_value_at_percentile.restype = ctypes.c_longlong
+ libfieldstat.fieldstat_histogram_value_at_percentile_api.argtypes = [ctypes.c_void_p, ctypes.c_double]
+ libfieldstat.fieldstat_histogram_value_at_percentile_api.restype = ctypes.c_longlong
- libfieldstat.fieldstat_histogram_count_le_value.argtypes = [ctypes.c_void_p, ctypes.c_longlong]
- libfieldstat.fieldstat_histogram_count_le_value.restype = ctypes.c_longlong
+ libfieldstat.fieldstat_histogram_count_le_value_api.argtypes = [ctypes.c_void_p, ctypes.c_longlong]
+ libfieldstat.fieldstat_histogram_count_le_value_api.restype = ctypes.c_longlong
libfieldstat.fieldstat_histogram_value_total_count.argtypes = [ctypes.c_void_p]
libfieldstat.fieldstat_histogram_value_total_count.restype = ctypes.c_longlong
@@ -134,7 +134,7 @@ class PrometheusExporter:
metrics = ""
for i in self.hist_bins:
- value = FieldstatAPI.libfieldstat.fieldstat_histogram_count_le_value(c_hist, int(i))
+ value = FieldstatAPI.libfieldstat.fieldstat_histogram_count_le_value_api(c_hist, int(i))
metric = name + "_bucket" + "{" + tags + ",le=\"{:.2f}\"".format(i) + "}" + ' ' + str(value) + '\n'
metrics += metric
self.n_lines += 1
@@ -144,7 +144,7 @@ class PrometheusExporter:
def __build_summary_format(self, name, tags, c_hist):
metrics = ""
for i in self.hist_bins:
- value = FieldstatAPI.libfieldstat.fieldstat_histogram_value_at_percentile(c_hist, float(i * 100))
+ value = FieldstatAPI.libfieldstat.fieldstat_histogram_value_at_percentile_api(c_hist, float(i * 100))
metric = name + "{" + tags + ",quantile=\"{:.2f}%\"".format(i * 100) + "}" + ' ' + str(value) + '\n'
metrics += metric
self.n_lines += 1
@@ -365,10 +365,10 @@ class HistogramTable:
row_values = []
for i in self.bins:
if self.format == "summary":
- value = FieldstatAPI.libfieldstat.fieldstat_histogram_value_at_percentile(c_hist, float(i * 100))
+ value = FieldstatAPI.libfieldstat.fieldstat_histogram_value_at_percentile_api(c_hist, float(i * 100))
row_values.append(str(value))
if self.format == "histogram":
- value = FieldstatAPI.libfieldstat.fieldstat_histogram_count_le_value(c_hist, int(i))
+ value = FieldstatAPI.libfieldstat.fieldstat_histogram_count_le_value_api(c_hist, int(i))
row_values.append(str(value))
shared_values = self.__get_row_shared_values(c_hist)
row_values += shared_values
@@ -930,7 +930,7 @@ class FieldstatExporter:
parser.add_argument("-m", "--match-tags", type = str, default = "",
help = "Display the tags match metrics")
parser.add_argument("-t", "--template", type = str, default = "",
- help = "Specify the print template with jinja2. For specific usage, try -th") # todo,在打印错误后给一个具体的帮助信息
+ help = "Specify the print template with jinja2. For specific usage, try -th")
parser.add_argument("-e", "--no-tagged", action = 'store_true', default = False,
help = "Print all counter metrics without tags in one list.")
diff --git a/src/fieldstat.c b/src/fieldstat.c
index 376bc43..bb530f1 100644
--- a/src/fieldstat.c
+++ b/src/fieldstat.c
@@ -6,126 +6,36 @@
#include "cjson/cJSON.h"
#include "uthash.h"
-#include "serializer.h"
+
#include "fieldstat.h"
#include "metrics/metric.h"
-#include "tags/cell_manager.h"
-
-#define DEFAULT_N_METRIC 64
-#define DEFAULT_N_CUBE 128
-
-struct fs_cube {
- enum sampling_mode sampling_mode;
- struct cell_manager *cell_manager;
- size_t max_n_cell;
- int next_cell_id;
- int primary_metric_id;
-
- // the key of cube is the combination of shared tags
- struct fieldstat_tag *shared_tags;
- size_t n_shared_tags;
- struct tag_hash_key *key_tag;
-
- struct metric **metrics;
- size_t valid_metric_arr_len;
- size_t max_n_metric;
-};
-
-struct metric_name_id_map {
- char *name;
- int id;
- UT_hash_handle hh;
-};
+#include "cube.h"
struct fieldstat {
- struct fs_cube **cube;
- unsigned long *cube_version; // increase from 0 every time the cube is deleted
- unsigned long cell_version; // increase from 0 every time fieldstat_reset is called
- size_t valid_cube_arr_length;
- size_t max_n_cube;
-
- struct metric **metric_masters;
- size_t n_metric_master;
- size_t max_n_metric_master;
- struct metric_name_id_map *metric_name_id_map;
-
- struct cube_manager *shared_tag_cube_manager;
+ struct cube_manager *cube_manager;
};
-int name_id_map_get_id_by_name(struct metric_name_id_map *map, const char *metric_name)
-{
- struct metric_name_id_map *entry = NULL;
- HASH_FIND_STR(map, metric_name, entry);
- if (entry == NULL) {
- return -1;
- }
- return entry->id;
-}
-
-void name_id_map_add(struct metric_name_id_map **map, const char *name, int id)
-{
- struct metric_name_id_map *entry = malloc(sizeof(struct metric_name_id_map));
- entry->id = id;
- entry->name = strdup(name);
- HASH_ADD_KEYPTR(hh, *map, entry->name, strlen(entry->name), entry);
-}
-
-void name_id_map_free(struct metric_name_id_map *map)
-{
- struct metric_name_id_map *entry, *tmp;
- HASH_ITER(hh, map, entry, tmp) {
- HASH_DEL(map, entry);
- free(entry->name);
- free(entry);
- }
-}
-
-struct metric_name_id_map *name_id_map_copy(struct metric_name_id_map *map)
-{
- struct metric_name_id_map *map_dup = NULL;
- struct metric_name_id_map *entry, *tmp;
- HASH_ITER(hh, map, entry, tmp) {
- name_id_map_add(&map_dup, entry->name, entry->id);
- }
- return map_dup;
-}
+/* -------------------------------------------------------------------------- */
+/* fieldstat */
+/* -------------------------------------------------------------------------- */
struct fieldstat *fieldstat_new()
{
struct fieldstat *instance = calloc(1, sizeof(struct fieldstat));
- instance->max_n_cube = DEFAULT_N_CUBE;
- instance->cube = calloc(instance->max_n_cube, sizeof(struct fs_cube *));
- instance->cube_version = calloc(instance->max_n_cube, sizeof(unsigned long));
-
- instance->max_n_metric_master = DEFAULT_N_METRIC;
- instance->metric_masters = calloc(instance->max_n_metric_master, sizeof(struct metric *));
- instance->metric_name_id_map = NULL;
-
- instance->shared_tag_cube_manager = cube_manager_new();
+ instance->cube_manager = cube_manager_new();
return instance;
}
-void fieldstat_cube_free(struct fieldstat *instance, int cube_id);
void fieldstat_free(struct fieldstat *instance)
{
if (instance == NULL) {
return;
}
- for (size_t i = 0; i < instance->valid_cube_arr_length; i++) {
- fieldstat_cube_free(instance, i);
- }
- free(instance->cube);
- free(instance->cube_version);
- cube_manager_free(instance->shared_tag_cube_manager);
- for (size_t i = 0; i < instance->n_metric_master; i++) {
- metric_free(instance->metric_masters[i]);
- }
- free(instance->metric_masters);
- name_id_map_free(instance->metric_name_id_map);
+ cube_manager_free(instance->cube_manager);
free(instance);
}
@@ -135,772 +45,163 @@ void fieldstat_reset(struct fieldstat *instance)
if (instance == NULL) {
return;
}
- for (size_t i = 0; i < instance->valid_cube_arr_length; i++) {
- struct fs_cube *cube = instance->cube[i];
- if (cube == NULL) {
- continue;
- }
- for (size_t j = 0; j < cube->valid_metric_arr_len; j++) {
- if (cube->metrics[j] == NULL) {
- continue;
- }
- metric_reset(cube->metrics[j]);
- }
-
- cell_manager_reset(cube->cell_manager);
- }
- instance->cell_version++;
-}
-
-unsigned long fieldstat_get_version(const struct fieldstat *instance)
-{
- if (instance == NULL) {
- return 0;
- }
- return instance->cell_version;
+ cube_manager_reset(instance->cube_manager);
}
-int fieldstat_destroy_cube(struct fieldstat *instance, int cube_id)
+int fieldstat_cube_destroy(struct fieldstat *instance, int cube_id)
{
- if (instance == NULL) {
- return FS_ERR_NULL_HANDLER;
- }
- if (cube_id < 0 || cube_id >= instance->valid_cube_arr_length) {
- return FS_ERR_INVALID_CUBE_ID;
- }
- if (instance->cube[cube_id] == NULL) {
+ struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
return FS_ERR_INVALID_CUBE_ID;
}
- const struct fs_cube *cube = instance->cube[cube_id];
- cube_manager_delete(instance->shared_tag_cube_manager, cube->key_tag);
-
- fieldstat_cube_free(instance, cube_id);
-
- instance->cube[cube_id] = NULL;
- instance->cube_version[cube_id]++;
- return 0;
-}
-
-long long fieldstat_get_cube_version(const struct fieldstat *instance, int cube_id)
-{
- if (instance == NULL) {
- return FS_ERR_NULL_HANDLER;
- }
- if (cube_id < 0 || cube_id >= instance->valid_cube_arr_length) {
- return FS_ERR_INVALID_CUBE_ID;
- }
- if (instance->cube[cube_id] == NULL && instance->cube_version[cube_id] == 0) {
- return FS_ERR_INVALID_CUBE_ID;
- }
+ cube_manager_delete(instance->cube_manager, cube);
- return instance->cube_version[cube_id];
+ return FS_OK;
}
/* -------------------------------------------------------------------------- */
/* cube */
/* -------------------------------------------------------------------------- */
-void fieldstat_free_tag_array(struct fieldstat_tag *tags, size_t n_tags)
-{
- for (size_t i = 0; i < n_tags; i++) {
- struct fieldstat_tag *tag = &tags[i];
- free((char *)tag->key);
- if (tag->type == TAG_CSTRING) {
- free((char *)tag->value_str);
- }
- }
- free(tags);
-}
-
-void add_cube_to_position(struct fieldstat *instance, struct fs_cube *cube, int cube_id)
-{
- if (cube_id >= instance->max_n_cube) {
- instance->max_n_cube *= 2;
- struct fs_cube **old_cube_arr = instance->cube;
- instance->cube = calloc(instance->max_n_cube, sizeof(struct fs_cube *));
- memcpy(instance->cube, old_cube_arr, sizeof(struct fs_cube *) * instance->valid_cube_arr_length);
- free(old_cube_arr);
-
- unsigned long *old_ver_arr = instance->cube_version;
- instance->cube_version = calloc(instance->max_n_cube, sizeof(unsigned long));
- memcpy(instance->cube_version, old_ver_arr, sizeof(unsigned long) * instance->valid_cube_arr_length);
- free(old_ver_arr);
- }
- instance->cube[cube_id] = cube;
- if (cube_id >= instance->valid_cube_arr_length) {
- instance->valid_cube_arr_length = cube_id + 1;
- }
-}
-
-int fieldstat_append_cube_to_instance(struct fieldstat *instance, struct fs_cube *cube)
-{
- for (int i = 0; i < instance->valid_cube_arr_length; i++) {
- if (instance->cube[i] == NULL) {
- instance->cube[i] = cube;
- cube_manager_add(instance->shared_tag_cube_manager, cube->key_tag, i);
- return i;
+// cppcheck-suppress [constParameterPointer, unmatchedSuppression]
+int fieldstat_cube_set_sampling(struct fieldstat *instance, int cube_id, enum sampling_mode mode, int max_n_cell, int primary_metric_id) {
+ if (max_n_cell <= 0) {
+ if (mode != SAMPLING_MODE_COMPREHENSIVE) {
+ return FS_ERR_MAX_N_CELL_LESS_THAN_ZERO;
+ } else {
+ max_n_cell = INT32_MAX;
}
}
- int cube_id = instance->valid_cube_arr_length;
- add_cube_to_position(instance, cube, cube_id);
- cube_manager_add(instance->shared_tag_cube_manager, cube->key_tag, cube_id);
-
- return cube_id;
-}
-struct fs_cube *fieldstat_cube_info_init(const struct fieldstat_tag *shared_tags, size_t n_tag, enum sampling_mode mode, size_t max_n_cell)
-{
- struct fs_cube *cube = calloc(1, sizeof(struct fs_cube));
- cube->sampling_mode = mode;
- cube->max_n_cell = max_n_cell;
- if (n_tag == 0) {
- cube->shared_tags = NULL;
- } else {
- cube->shared_tags = malloc(sizeof(struct fieldstat_tag) * n_tag);
- for (int i = 0; i < n_tag; i++) {
- struct fieldstat_tag *dest_tag = &cube->shared_tags[i];
- dest_tag->key = strdup(shared_tags[i].key);
- dest_tag->type = shared_tags[i].type;
- switch (dest_tag->type)
- {
- case TAG_INTEGER:
- dest_tag->value_longlong = shared_tags[i].value_longlong;
- break;
- case TAG_CSTRING:
- dest_tag->value_str = strdup(shared_tags[i].value_str);
- break;
- case TAG_DOUBLE:
- dest_tag->value_double = shared_tags[i].value_double;
- break;
-
- default:
- break;
- }
- }
+ struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
+ return FS_ERR_INVALID_CUBE_ID;
}
-
- cube->n_shared_tags = n_tag;
-
- struct tag_hash_key *shared_tag_key = malloc(sizeof(struct tag_hash_key));
- tag_hash_key_init_with_fieldstat_tag(shared_tag_key, shared_tags, n_tag, true);
- cube->key_tag = shared_tag_key;
-
- cube->max_n_metric = 64;
- cube->metrics = calloc(cube->max_n_metric, sizeof(struct metric *));
-
- return cube;
-}
-
-struct fs_cube *fieldstat_cube_new(const struct fieldstat_tag *shared_tags, size_t n_tag, enum sampling_mode mode, size_t max_n_cell)
-{
- struct fs_cube *cube = fieldstat_cube_info_init(shared_tags, n_tag, mode, max_n_cell);
-
- cube->cell_manager = cell_manager_new(mode, max_n_cell);
-
- return cube;
+
+ return cube_set_sampling(cube, mode, max_n_cell, primary_metric_id);
}
-int fieldstat_create_cube(struct fieldstat *instance, const struct fieldstat_tag *shared_tags, size_t n_tag, enum sampling_mode mode, size_t max_n_cell)
+int fieldstat_cube_create(struct fieldstat *instance, const struct field *cube_dimensions, size_t n_dimension)
{
if (instance == NULL) {
return FS_ERR_NULL_HANDLER;
}
- if (n_tag == 0 || shared_tags == NULL) {
- shared_tags = NULL;
- n_tag = 0;
- }
- if (mode == SAMPLING_MODE_TOPK && max_n_cell == 0) {
- return FS_ERR_INVALID_PARAM;
- }
- if (max_n_cell == 0) {
- max_n_cell = INT32_MAX;
- }
- struct tag_hash_key shared_tag_key;
- tag_hash_key_init_with_fieldstat_tag(&shared_tag_key, shared_tags, n_tag, false);
- int ret = cube_manager_find(instance->shared_tag_cube_manager, &shared_tag_key);
- if (ret != -1) {
- return FS_ERR_INVALID_KEY;
- }
-
- struct fs_cube *cube = fieldstat_cube_new(shared_tags, n_tag, mode, max_n_cell);
-
- int cube_id = fieldstat_append_cube_to_instance(instance, cube);
-
- return cube_id;
-}
-
-int fieldstat_cube_add(struct fieldstat *instance, int cube_id, const struct fieldstat_tag *tags, size_t n_tag, long long occurrence)
-{
- struct fs_cube *cube = instance->cube[cube_id];
-
- int ret = -1;
- struct tag_hash_key tag_key;
-
- tag_hash_key_init_with_fieldstat_tag(&tag_key, tags, n_tag, false);
- if (cube->sampling_mode == SAMPLING_MODE_COMPREHENSIVE) {
- ret = cell_manager_add_cell(cube->cell_manager, &tag_key);
- } else {
- int popped_cell_id = -1;
- ret = cell_manager_add_cell_topk(cube->cell_manager, &tag_key, occurrence, &popped_cell_id);
-
- if (popped_cell_id != -1) {
- for (size_t i = 0; i < cube->valid_metric_arr_len; i++) {
- if (cube->metrics[i] == NULL) {
- continue;
- }
- metric_delete_cell(cube->metrics[i], popped_cell_id);
- }
- }
+ if (n_dimension == 0 || cube_dimensions == NULL) {
+ cube_dimensions = NULL;
+ n_dimension = 0;
}
+ struct cube *cube = cube_new(cube_dimensions, n_dimension);
+ int ret = cube_manager_add(instance->cube_manager, cube);
if (ret < 0) {
- return -1;
- }
- return ret;
-}
-
-void fieldstat_cube_free_contents(struct fieldstat *instance, int cube_id)
-{
- struct fs_cube *cube = instance->cube[cube_id];
-
- cell_manager_free(cube->cell_manager);
- fieldstat_free_tag_array(cube->shared_tags, cube->n_shared_tags);
- tag_hash_key_free(cube->key_tag);
-
- for (size_t i = 0; i < cube->valid_metric_arr_len; i++) {
- if (cube->metrics[i] != NULL) {
- metric_free(cube->metrics[i]);
- }
- }
- free(cube->metrics);
-
- free(cube);
- instance->cube[cube_id] = NULL;
-
- if (cube_id == instance->valid_cube_arr_length - 1) {
- instance->valid_cube_arr_length--;
- }
-}
-
-void fieldstat_cube_free(struct fieldstat *instance, int cube_id)
-{
- if (instance->cube[cube_id] == NULL) {
- return;
- }
-
- cube_manager_delete(instance->shared_tag_cube_manager, instance->cube[cube_id]->key_tag);
-
- fieldstat_cube_free_contents(instance, cube_id);
-}
-
-struct fs_cube *fieldstat_cube_fork(const struct fs_cube *cube) {
- struct fs_cube *ret = fieldstat_cube_new(cube->shared_tags, cube->n_shared_tags, cube->sampling_mode, cube->max_n_cell);
- ret->primary_metric_id = cube->primary_metric_id;
-
- return ret;
-}
-
-int fieldstat_cube_set_primary_metric(struct fieldstat *instance, int cube_id, int metric_id)
-{
- if (instance == NULL) {
- return FS_ERR_NULL_HANDLER;
- }
- if (cube_id < 0 || cube_id >= instance->valid_cube_arr_length) {
- return FS_ERR_INVALID_CUBE_ID;
+ cube_free(cube);
+ return FS_ERR_DIMENSION_ALREADY_EXISTS;
}
- struct fs_cube *cube = instance->cube[cube_id];
- if (cube == NULL) {
- return FS_ERR_INVALID_CUBE_ID;
- }
- if (cube->sampling_mode != SAMPLING_MODE_TOPK) {
- return FS_ERR_INVALID_PARAM;
- }
- if (metric_id < 0 || metric_id >= instance->n_metric_master) {
- return FS_ERR_INVALID_METRIC_ID;
- }
- if (instance->metric_masters[metric_id] == NULL) {
- return FS_ERR_INVALID_METRIC_ID;
- }
- if (metric_get_type(instance->metric_masters[metric_id]) != METRIC_TYPE_COUNTER) {
- return FS_ERR_INVALID_PARAM;
- }
-
- cube->primary_metric_id = metric_id;
- return FS_OK;
+ return ret; //ret is the cube_id
}
/* -------------------------------------------------------------------------- */
/* metric register */
/* -------------------------------------------------------------------------- */
-
-void add_metric_to_cube(struct fs_cube *cube, struct metric *metric, int metric_id)
-{
- if (metric_id >= cube->max_n_metric) {
- cube->max_n_metric *= 2;
- cube->metrics = realloc(cube->metrics, sizeof(struct metric *) * cube->max_n_metric);
- memset(cube->metrics + cube->valid_metric_arr_len, 0, sizeof(struct metric *) * (cube->max_n_metric - cube->valid_metric_arr_len));
- }
-
- cube->metrics[metric_id] = metric;
- if (metric_id >= cube->valid_metric_arr_len) {
- cube->valid_metric_arr_len = metric_id + 1;
- }
-}
-
-void add_metric_to_instance(struct fieldstat *instance, const struct metric *metric, int metric_id)
-{
- if (metric_id >= instance->max_n_metric_master) {
- instance->max_n_metric_master *= 2;
- instance->metric_masters = realloc(instance->metric_masters, sizeof(struct metric *) * instance->max_n_metric_master);
- memset(instance->metric_masters + instance->n_metric_master, 0, sizeof(struct metric *) * (instance->max_n_metric_master - instance->n_metric_master));
- }
-
- instance->metric_masters[metric_id] = (struct metric *)metric;
- if (metric_id >= instance->n_metric_master) {
- instance->n_metric_master = metric_id + 1;
- }
- name_id_map_add(&instance->metric_name_id_map, metric_get_name(metric), metric_id);
-}
-
-struct metric *find_or_add_metric(struct fieldstat *instance, int cube_id, int metric_id) {
- struct fs_cube *cube = instance->cube[cube_id];
- if (metric_id < cube->valid_metric_arr_len && cube->metrics[metric_id] != NULL) {
- return cube->metrics[metric_id];
- }
- struct metric *metric = metric_fork(instance->metric_masters[metric_id]);
- add_metric_to_cube(cube, metric, metric_id);
- return metric;
-}
-
-static int append_metric_to_instance(struct fieldstat *instance, const struct metric *metric)
-{
- int metric_id = instance->n_metric_master;
- add_metric_to_instance(instance, metric, metric_id);
-
- return metric_id;
-}
-
-int check_before_register_metric(const struct fieldstat *instance, const char *metric_name)
-{
- if (instance == NULL) {
- return FS_ERR_NULL_HANDLER;
- }
-
- if (name_id_map_get_id_by_name(instance->metric_name_id_map, metric_name) != -1) {
- return FS_ERR_INVALID_KEY;
- }
-
- return FS_OK;
-}
-
-int fieldstat_register_counter(struct fieldstat *instance, const char *metric_name)
+// cppcheck-suppress [constParameterPointer, unmatchedSuppression]
+int fieldstat_register_counter(struct fieldstat *instance, int cube_id, const char *metric_name)
{
- int ret = check_before_register_metric(instance, metric_name);
- if (ret != FS_OK) {
- return ret;
+ struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
+ return FS_ERR_INVALID_CUBE_ID;
}
-
- const struct metric *metric = metric_counter_new(metric_name);
-
- return append_metric_to_instance(instance, metric);
+ return cube_register_counter(cube, metric_name);
}
-int fieldstat_register_hll(struct fieldstat *instance, const char *metric_name, unsigned char precision)
+// cppcheck-suppress [constParameterPointer, unmatchedSuppression]
+int fieldstat_register_hll(struct fieldstat *instance, int cube_id, const char *metric_name, unsigned char precision)
{
- int ret = check_before_register_metric(instance, metric_name);
- if (ret != FS_OK) {
- return ret;
- }
- if (precision < 4 || precision > 18) {
- return FS_ERR_INVALID_PARAM;
+ struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
+ return FS_ERR_INVALID_CUBE_ID;
}
-
- const struct metric *metric = metric_hll_new(metric_name, precision);
-
- return append_metric_to_instance(instance, metric);
+ return cube_register_hll(cube, metric_name, precision);
}
-int fieldstat_register_hist(struct fieldstat *instance, const char *metric_name, long long lowest_trackable_value, long long highest_trackable_value, int significant_figures)
+// cppcheck-suppress [constParameterPointer, unmatchedSuppression]
+int fieldstat_register_histogram(struct fieldstat *instance, int cube_id, const char *metric_name, long long lowest_trackable_value, long long highest_trackable_value, int significant_figures)
{
- int ret = check_before_register_metric(instance, metric_name);
- if (ret != FS_OK) {
- return ret;
- }
- // refer to hdr_histogram.h for the rules of parameters. Just copy them here
- if (lowest_trackable_value < 1) {
- return FS_ERR_INVALID_PARAM;
- }
- if (significant_figures < 1 || significant_figures > 5) {
- return FS_ERR_INVALID_PARAM;
+ struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
+ return FS_ERR_INVALID_CUBE_ID;
}
- if (lowest_trackable_value * 2 > highest_trackable_value)
- {
- return FS_ERR_INVALID_PARAM;
- }
-
- const struct metric *metric = metric_histogram_new(metric_name, lowest_trackable_value, highest_trackable_value, significant_figures);
-
- return append_metric_to_instance(instance, metric);
+ return cube_register_hist(cube, metric_name, lowest_trackable_value, highest_trackable_value, significant_figures);
}
/* -------------------------------------------------------------------------- */
/* metric operation */
/* -------------------------------------------------------------------------- */
-static struct metric *fieldstat_find_metric(const struct fieldstat *instance, int cube_id, int metric_id, int *err_code)
+// cppcheck-suppress [constParameterPointer, unmatchedSuppression]
+int fieldstat_counter_incrby(struct fieldstat *instance, int cube_id, int metric_id, const struct field *cell_dimensions, size_t n_dimensions, long long increment)
{
- if (instance == NULL) {
- *err_code = FS_ERR_NULL_HANDLER;
- return NULL;
- }
- if (cube_id < 0 || cube_id >= instance->valid_cube_arr_length) {
- *err_code = FS_ERR_INVALID_CUBE_ID;
- return NULL;
- }
- struct fs_cube *cube = instance->cube[cube_id];
+ struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
if (cube == NULL) {
- *err_code = FS_ERR_INVALID_CUBE_ID;
- return NULL;
- }
-
- *err_code = FS_OK;
- return cube->metrics[metric_id];
-}
-
-int check_before_add(const struct fieldstat *instance, int cube_id, int metric_id, enum metric_type type)
-{
- if (instance == NULL) {
- return FS_ERR_NULL_HANDLER;
- }
-
- if (cube_id < 0 || cube_id >= instance->valid_cube_arr_length) {
return FS_ERR_INVALID_CUBE_ID;
}
- if (instance->cube[cube_id] == NULL) {
- return FS_ERR_INVALID_CUBE_ID;
- }
-
- if (metric_id < 0 || metric_id >= instance->n_metric_master) {
- return FS_ERR_INVALID_METRIC_ID;
- }
- const struct metric *metric = instance->metric_masters[metric_id];
- if (metric == NULL || metric_get_type(metric) != type) {
- return FS_ERR_INVALID_METRIC_ID;
- }
-
- return FS_OK;
-}
-
-int fieldstat_counter_incrby(struct fieldstat *instance, int cube_id, int metric_id, const struct fieldstat_tag *tags, size_t n_tag, long long increment)
-{
- int ret = check_before_add(instance, cube_id, metric_id, METRIC_TYPE_COUNTER);
- if (ret != FS_OK) {
- return ret;
- }
- const struct fs_cube *cube = instance->cube[cube_id];
- long long occurrence = 0;
- if (cube->sampling_mode == SAMPLING_MODE_TOPK && cube->primary_metric_id == metric_id) {
- if (increment < 0) {
- return FS_ERR_INVALID_PARAM;
- }
-
- occurrence = increment;
- }
-
- int cell_id = fieldstat_cube_add(instance, cube_id, tags, n_tag, occurrence);
- if (cell_id < 0) {
- return FS_ERR_TOO_MANY_CELLS;
- }
-
- struct metric *metric = find_or_add_metric(instance, cube_id, metric_id);
-
- metric_counter_incrby(metric, cell_id, increment);
- return FS_OK;
-}
-
-int fieldstat_counter_incrby_batch(struct fieldstat *instance, int cube_id, int metric_ids[], const struct fieldstat_tag *tags, size_t n_tag, long long increments[], int n_metric)
-{
- int ret = check_before_add(instance, cube_id, metric_ids[0], METRIC_TYPE_COUNTER);
- if (ret != FS_OK) {
- return ret;
- }
- const struct fs_cube *cube = instance->cube[cube_id];
- long long occurrence = 0;
- if (cube->sampling_mode == SAMPLING_MODE_TOPK) {
- for (int i = 0; i < n_metric; i++) {
- if (metric_ids[i] == cube->primary_metric_id) {
- if (increments[i] < 0) {
- return FS_ERR_INVALID_PARAM;
- }
- occurrence += increments[i];
- }
- }
- }
- int cell_id = fieldstat_cube_add(instance, cube_id, tags, n_tag, occurrence);
- if (cell_id < 0) {
- return FS_ERR_TOO_MANY_CELLS;
- }
-
- for (int i = 0; i < n_metric; i++) {
- struct metric *metric = find_or_add_metric(instance, cube_id, metric_ids[i]);
- metric_counter_incrby(metric, cell_id, increments[i]);
- }
- return FS_OK;
-}
-
-int fieldstat_counter_set(struct fieldstat *instance, int cube_id, int metric_id, const struct fieldstat_tag *tags, size_t n_tag, long long value)
-{
- int ret = check_before_add(instance, cube_id, metric_id, METRIC_TYPE_COUNTER);
- if (ret != FS_OK) {
- return ret;
- }
- const struct fs_cube *cube = instance->cube[cube_id];
- struct metric *metric = find_or_add_metric(instance, cube_id, metric_id);
- int cell_id = -1;
-
- // get the occurrence
- long long occurrence = 0;
- if (cube->sampling_mode == SAMPLING_MODE_TOPK && cube->primary_metric_id == metric_id) {
- struct tag_hash_key tag_key;
- tag_hash_key_init_with_fieldstat_tag(&tag_key, tags, n_tag, false);
- cell_id = cell_manager_find(cube->cell_manager, &tag_key);
- if (cell_id >= 0) {
- long long old_value;
- int exist_flag = metric_counter_get(metric, cell_id, &old_value);
- if (exist_flag == 0) { // cell already exist in metric
- occurrence = value - old_value;
- } else {
- occurrence = value;
- }
- } else {
- occurrence = value;
- }
- if (occurrence < 0) {
- return FS_ERR_INVALID_PARAM;
- }
- }
-
- cell_id = fieldstat_cube_add(instance, cube_id, tags, n_tag, occurrence);
- if (cell_id < 0) {
- return FS_ERR_TOO_MANY_CELLS;
- }
- metric_counter_set(metric, cell_id, value);
- return 0;
+ return cube_counter_incrby(cube, metric_id, cell_dimensions, n_dimensions, increment);
}
-int fieldstat_hll_add(struct fieldstat *instance, int cube_id, int metric_id, const struct fieldstat_tag *tags, size_t n_tag, const char *key, size_t key_len)
+// cppcheck-suppress [constParameterPointer, unmatchedSuppression]
+int fieldstat_counter_set(struct fieldstat *instance, int cube_id, int metric_id, const struct field *cell_dimensions, size_t n_dimensions, long long value)
{
- int ret = check_before_add(instance, cube_id, metric_id, METRIC_TYPE_HLL);
- if (ret != FS_OK) {
- return ret;
- }
- if (instance->cube[cube_id]->sampling_mode == SAMPLING_MODE_TOPK) {
- return FS_ERR_INVALID_PARAM;
- }
- int cell_id = fieldstat_cube_add(instance, cube_id, tags, n_tag, 0);
- if (cell_id < 0) {
- return FS_ERR_TOO_MANY_CELLS;
- }
- struct metric *metric = find_or_add_metric(instance, cube_id, metric_id);
-
- metric_hll_add(metric, cell_id, key, key_len);
- return 0;
-}
-
-int fieldstat_hist_record(struct fieldstat *instance, int cube_id, int metric_id, const struct fieldstat_tag *tags, size_t n_tag, long long value)
-{
- int ret = check_before_add(instance, cube_id, metric_id, METRIC_TYPE_HISTOGRAM);
- if (ret != FS_OK) {
- return ret;
- }
- if (instance->cube[cube_id]->sampling_mode == SAMPLING_MODE_TOPK) {
- return FS_ERR_INVALID_PARAM;
- }
-
- int cell_id = fieldstat_cube_add(instance, cube_id, tags, n_tag, 0);
- if (cell_id < 0) {
- return FS_ERR_TOO_MANY_CELLS;
+ struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
+ return FS_ERR_INVALID_CUBE_ID;
}
- struct metric *metric = find_or_add_metric(instance, cube_id, metric_id);
- ret = metric_histogram_record(metric, cell_id, value);
- if (ret < 0) { // it's ok not to recover the cell and metrics even if they are new ones, since unused ones will not be exported.
- return FS_ERR_INVALID_PARAM;
- }
- return 0;
+ return cube_counter_set(cube, metric_id, cell_dimensions, n_dimensions, value);
}
-
-/* -------------------------------------------------------------------------- */
-/* merge */
-/* -------------------------------------------------------------------------- */
-
-struct fs_cube *fieldstat_cube_dup(const struct fs_cube *cube, const int *metric_id_map)
+// cppcheck-suppress [constParameterPointer, unmatchedSuppression]
+int fieldstat_hll_add(struct fieldstat *instance, int cube_id, int metric_id, const struct field *cell_dimensions, size_t n_dimensions, const char *key, size_t key_len)
{
- struct fs_cube *cube_dup = fieldstat_cube_info_init(cube->shared_tags, cube->n_shared_tags, cube->sampling_mode, cube->max_n_cell);
- const struct cell_manager *cm_src = cube->cell_manager;
-
- struct cell_manager *cm_dup = cell_manager_copy(cm_src);
- cube_dup->cell_manager = cm_dup;
- cube_dup->primary_metric_id = metric_id_map[cube->primary_metric_id];
-
- for (int i = 0; i < cube->valid_metric_arr_len; i ++) {
- if (cube->metrics[i] == NULL) {
- continue;
- }
- struct metric *metric_dup = metric_copy(cube->metrics[i]);
- add_metric_to_cube(cube_dup, metric_dup, metric_id_map[i]);
+ struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
+ return FS_ERR_INVALID_CUBE_ID;
}
- return cube_dup;
-}
-
-void fieldstat_cube_merge_comprehensive(struct fs_cube *dest, const struct fs_cube *src, const int *metric_id_src_dest_map)
-{
- const struct cell_manager *cell_manager_src = src->cell_manager;
- struct cell_manager *cell_manager_dest = dest->cell_manager;
-
- int arr_len_src = 0;
- const struct tag_hash_key **tag_arr_src = cell_manager_dump(cell_manager_src, &arr_len_src);
- for (int cell_id_src = 0; cell_id_src < arr_len_src; cell_id_src++) {
- const struct tag_hash_key *tag_src = tag_arr_src[cell_id_src];
- if (tag_src == NULL) {
- continue;
- }
- int cell_id_final = cell_manager_add_cell(cell_manager_dest, tag_src);
- if (cell_id_final == -1) { // dest is full
- break;
- }
- for (int metric_id_src = 0; metric_id_src < src->valid_metric_arr_len; metric_id_src++) {
- if (src->metrics[metric_id_src] == NULL) {
- continue;
- }
- int metric_id_dest = metric_id_src_dest_map[metric_id_src];
- (void)metric_merge_or_copy_cell(dest->metrics[metric_id_dest], src->metrics[metric_id_src], cell_id_final, cell_id_src);
- }
- }
+ return cube_hll_add(cube, metric_id, cell_dimensions, n_dimensions, key, key_len);
}
-void fieldstat_cube_merge_topk(struct fs_cube *dest, const struct fs_cube *src, const int *metric_id_src_dest_map)
+// cppcheck-suppress [constParameterPointer, unmatchedSuppression]
+int fieldstat_hll_add_field(struct fieldstat *instance, int cube_id, int metric_id, const struct field *cell_dimensions, size_t n_dimensions, const struct field *item, size_t item_len)
{
- const struct cell_manager *cell_manager_src = src->cell_manager;
- struct cell_manager *cell_manager_dest = dest->cell_manager;
- int *cell_id_added = NULL;
- int *cell_id_old = NULL;
- int n_cell_id_added = 0;
- int *cell_id_popped = NULL;
- int n_cell_id_popped = 0;
-
- cell_manager_merge_topk(cell_manager_dest, cell_manager_src, &cell_id_popped, &n_cell_id_popped, &cell_id_old, &cell_id_added, &n_cell_id_added);
-
- for (int i = 0; i < n_cell_id_added; i++) {
- int tmp_id_dest = cell_id_added[i];
- int tmp_id_src = cell_id_old[i];
-
- for (int j = 0; j < src->valid_metric_arr_len; j++) {
- if (src->metrics[j] == NULL) {
- continue;
- }
- int metric_id_dest = metric_id_src_dest_map[j];
- metric_merge_or_copy_cell(dest->metrics[metric_id_dest], src->metrics[j], tmp_id_dest, tmp_id_src);
- }
- }
- // chances are that: a cell exists in both dest and src, but it is not in final. In this case, these cells are both in cell_id_added and cell_id_popped.
- // Since all cells are counter, which is easy to merge, we just delete these cells after merging them.
- for (int i = 0; i < n_cell_id_popped;i++) {
- int id = cell_id_popped[i];
-
- for (int j = 0; j < dest->valid_metric_arr_len; j++) {
- if (dest->metrics[j] == NULL) {
- continue;
- }
- metric_delete_cell(dest->metrics[j], id);
- }
+ struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
+ return FS_ERR_INVALID_CUBE_ID;
}
- free(cell_id_popped);
- free(cell_id_added);
- free(cell_id_old);
+ return cube_hll_add_field(cube, metric_id, cell_dimensions, n_dimensions, item, item_len);
}
-void fieldstat_cube_merge(struct fs_cube *dest, const struct fs_cube *src, const int *metric_id_src_dest_map)
+// cppcheck-suppress [constParameterPointer, unmatchedSuppression]
+int fieldstat_histogram_record(struct fieldstat *instance, int cube_id, int metric_id, const struct field *cell_dimensions, size_t n_dimensions, long long value)
{
- for (int metric_id_src = 0; metric_id_src < src->valid_metric_arr_len; metric_id_src++) {
- if (src->metrics[metric_id_src] == NULL) {
- continue;
- }
-
- int metric_id_dest = metric_id_src_dest_map[metric_id_src];
- if (dest->metrics[metric_id_dest] != NULL) {
- continue;
- }
-
- struct metric *metric_dest = metric_fork(src->metrics[metric_id_src]);
- add_metric_to_cube(dest, metric_dest, metric_id_dest);
+ struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
+ return FS_ERR_INVALID_CUBE_ID;
}
- if (dest->sampling_mode == SAMPLING_MODE_COMPREHENSIVE) {
- return fieldstat_cube_merge_comprehensive(dest, src, metric_id_src_dest_map);
- }
- fieldstat_cube_merge_topk(dest, src, metric_id_src_dest_map);
+ return cube_histogram_record(cube, metric_id, cell_dimensions, n_dimensions, value);
}
-int fieldstat_merge(struct fieldstat *instance, struct fieldstat *src)
+int fieldstat_merge(struct fieldstat *instance, const struct fieldstat *src)
{
if (instance == NULL || src == NULL) {
return FS_ERR_NULL_HANDLER;
}
- int metric_id_src_dest_map[src->n_metric_master]; // every metric <src_id> in src move to metric_id_src_dest_map[<src_id>] in dst instance
- for (int metric_id_src = 0; metric_id_src < src->n_metric_master; metric_id_src++) {
- const char *name_src = metric_get_name(src->metric_masters[metric_id_src]);
- int metric_id_dst = name_id_map_get_id_by_name(instance->metric_name_id_map, name_src);
- if (metric_id_dst == -1) {
- metric_id_dst = append_metric_to_instance(instance, metric_fork(src->metric_masters[metric_id_src]));
- name_id_map_add(&instance->metric_name_id_map, name_src, metric_id_dst);
- }
- metric_id_src_dest_map[metric_id_src] = metric_id_dst;
- }
-
- size_t n_cube_src = src->valid_cube_arr_length;
- const struct cube_manager *tag_cube_id_map = instance->shared_tag_cube_manager;
- int ret = 0;
- for (int i = 0; i < n_cube_src; i++) {
- const struct fs_cube *cube_src = src->cube[i];
- if (cube_src == NULL) {
- continue;
- }
-
- const struct tag_hash_key *shared_tag_key_src = cube_src->key_tag;
- int cube_id_tmp = cube_manager_find(tag_cube_id_map, shared_tag_key_src);
- if (cube_id_tmp == -1) {
- struct fs_cube *copied_cube = fieldstat_cube_dup(cube_src, metric_id_src_dest_map);
- fieldstat_append_cube_to_instance(instance, copied_cube);
- } else {
- struct fs_cube *cube_dst = instance->cube[cube_id_tmp];
- if (cube_dst->sampling_mode != cube_src->sampling_mode) {
- ret = FS_ERR_INVALID_PARAM;
- continue;
- }
- if (cube_dst->sampling_mode == SAMPLING_MODE_TOPK && strcmp(
- metric_get_name(instance->metric_masters[cube_dst->primary_metric_id]),
- metric_get_name(src->metric_masters[cube_src->primary_metric_id])
- ) != 0) {
- printf("primary metric name not match, name instance: %s, name src: %s\n",
- metric_get_name(instance->metric_masters[cube_dst->primary_metric_id]),
- metric_get_name(src->metric_masters[cube_src->primary_metric_id]));
- ret = FS_ERR_INVALID_PARAM;
- continue;
- }
- fieldstat_cube_merge(cube_dst, cube_src, metric_id_src_dest_map);
- }
- }
-
- return ret;
+ return cube_manager_merge(instance->cube_manager, src->cube_manager);
}
struct fieldstat *fieldstat_fork(const struct fieldstat *instance)
@@ -909,132 +210,14 @@ struct fieldstat *fieldstat_fork(const struct fieldstat *instance)
return NULL;
}
struct fieldstat *new_instance = calloc(1, sizeof(struct fieldstat));
- new_instance->shared_tag_cube_manager = cube_manager_new();
-
- new_instance->valid_cube_arr_length = instance->valid_cube_arr_length;
- new_instance->max_n_cube = instance->max_n_cube;
- new_instance->cube = calloc(new_instance->max_n_cube, sizeof(struct fs_cube *));
- for (size_t i = 0; i < new_instance->valid_cube_arr_length; i++) {
- struct fs_cube *cube = instance->cube[i];
- if (cube == NULL) {
- continue;
- }
- new_instance->cube[i] = fieldstat_cube_fork(cube);
- cube_manager_add(new_instance->shared_tag_cube_manager, cube->key_tag, i);
- // copy registered metrics
- for (size_t j = 0; j < cube->valid_metric_arr_len; j++) {
- const struct metric *metric = cube->metrics[j];
- if (metric == NULL) {
- continue;
- }
- struct metric *new_metric = metric_fork(metric);
- add_metric_to_cube(new_instance->cube[i], new_metric, j);
- }
- }
- new_instance->cube_version = calloc(new_instance->max_n_cube, sizeof(unsigned long));
- memcpy(new_instance->cube_version, instance->cube_version, sizeof(unsigned long) * new_instance->max_n_cube);
-
- new_instance->metric_masters = calloc(instance->max_n_metric_master, sizeof(struct metric *));
- new_instance->max_n_metric_master = instance->max_n_metric_master;
- new_instance->n_metric_master = instance->n_metric_master;
- for (size_t i = 0; i < instance->n_metric_master; i++) {
- new_instance->metric_masters[i] = metric_fork(instance->metric_masters[i]);
- }
- new_instance->metric_name_id_map = name_id_map_copy(instance->metric_name_id_map);
+ new_instance->cube_manager = cube_manager_fork(instance->cube_manager);
return new_instance;
}
-void calibrate_metrics_in_instance(const struct fieldstat *master, struct fieldstat *replica)
+void fieldstat_calibrate(const struct fieldstat *master, struct fieldstat *replica)
{
- if (replica->max_n_metric_master < master->max_n_metric_master) {
- replica->metric_masters = (struct metric **)realloc(replica->metric_masters, sizeof(struct metric *) * master->max_n_metric_master);
- memset(replica->metric_masters + replica->max_n_metric_master, 0, sizeof(struct metric *) * (master->max_n_metric_master - replica->max_n_metric_master));
- replica->max_n_metric_master = master->max_n_metric_master;
- }
-
- size_t longer_arr_len = master->n_metric_master > replica->n_metric_master ? master->n_metric_master : replica->n_metric_master;
- for (size_t i = 0; i < longer_arr_len; i++) {
- const struct metric *metric_master = i >= master->n_metric_master ? NULL : master->metric_masters[i];
- struct metric *metric_target = i >= replica->n_metric_master ? NULL : replica->metric_masters[i];
- if (metric_master == NULL && metric_target == NULL) {
- continue;
- }
- if (metric_master == NULL && metric_target != NULL) {
- metric_free(metric_target);
- replica->metric_masters[i] = NULL;
- continue;
- }
- if (metric_master != NULL && metric_target == NULL) {
- const struct metric *metric_dup = metric_fork(metric_master);
- add_metric_to_instance(replica, metric_dup, i);
- continue;
- }
- if (metric_get_type(metric_master) != metric_get_type(metric_target) ||
- strcmp(metric_get_name(metric_master), metric_get_name(metric_target)) != 0
- ) {
- metric_free(metric_target);
- const struct metric *metric_dup = metric_fork(metric_master);
- add_metric_to_instance(replica, metric_dup, i);
- continue;
- }
-
- // metric same, no need to do anything
- }
-
- replica->n_metric_master = master->n_metric_master;
-}
-
-int fieldstat_calibrate(const struct fieldstat *master, struct fieldstat *replica)
-{
- if (master == NULL || replica == NULL) {
- return FS_ERR_NULL_HANDLER;
- }
-
- if (replica->max_n_cube < master->max_n_cube) {
- replica->cube = (struct fs_cube **)realloc(replica->cube, sizeof(struct fs_cube *) * master->max_n_cube);
- memset(replica->cube + replica->max_n_cube, 0, sizeof(struct fs_cube *) * (master->max_n_cube - replica->max_n_cube));
- replica->cube_version = (unsigned long *)realloc(replica->cube_version, sizeof(unsigned long) * master->max_n_cube);
- memset(replica->cube_version + replica->max_n_cube, 0, sizeof(unsigned long) * (master->max_n_cube - replica->max_n_cube));
- replica->max_n_cube = master->max_n_cube;
- }
-
- size_t len_master = master->valid_cube_arr_length;
- size_t len_replica = replica->valid_cube_arr_length;
- size_t longer_arr_len = len_master > len_replica ? len_master : len_replica;
- for (size_t i = 0; i < longer_arr_len; i++) {
- const struct fs_cube *cube_master = i >= len_master ? NULL : master->cube[i];
- const struct fs_cube *cube_target = i >= len_replica ? NULL : replica->cube[i];
-
- if (cube_master == NULL && cube_target == NULL) {
- continue;
- }
- if (cube_master == NULL && cube_target != NULL) {
- fieldstat_cube_free_contents(replica, i);
- continue;
- }
- if (cube_master != NULL && cube_target == NULL) {
- struct fs_cube *cube_dup = fieldstat_cube_fork(cube_master);
- add_cube_to_position(replica, cube_dup, i);
- continue;
- }
-
- if (master->cube_version[i] == replica->cube_version[i] && tag_hash_key_cmp(cube_master->key_tag, cube_target->key_tag) == 0) {
- continue;
- }
-
- fieldstat_cube_free_contents(replica, i);
- struct fs_cube *cube_dup = fieldstat_cube_fork(cube_master);
- add_cube_to_position(replica, cube_dup, i);
- }
-
- memcpy(replica->cube_version, master->cube_version, sizeof(unsigned long) * master->max_n_cube);
- replica->valid_cube_arr_length = master->valid_cube_arr_length;
-
- cube_manager_calibrate(replica->shared_tag_cube_manager, master->shared_tag_cube_manager);
- calibrate_metrics_in_instance(master, replica);
-
- return FS_OK;
+ cube_manager_calibrate(replica->cube_manager, master->cube_manager);
}
/* -------------------------------------------------------------------------- */
@@ -1042,374 +225,128 @@ int fieldstat_calibrate(const struct fieldstat *master, struct fieldstat *replic
/* -------------------------------------------------------------------------- */
void fieldstat_get_cubes(const struct fieldstat *instance, int **cube_ids, int *n_cube)
{
- if (instance == NULL || instance->valid_cube_arr_length == 0) {
- *cube_ids = NULL;
- *n_cube = 0;
- return;
- }
- int all_available_cube_count = 0;
-
- int *tmp_ids = (int *)malloc(sizeof(int) * instance->valid_cube_arr_length);
- for (int i = 0; i < instance->valid_cube_arr_length; i++) {
- const struct fs_cube *tmp_cube = instance->cube[i];
- if (tmp_cube == NULL) {
- continue;
- }
- tmp_ids[all_available_cube_count] = i;
- all_available_cube_count ++;
- }
- if (all_available_cube_count == 0) {
- free(tmp_ids);
- *cube_ids = NULL;
- *n_cube = 0;
- return;
- }
-
- *cube_ids = (int *)malloc(sizeof(int) * all_available_cube_count);
- memcpy(*cube_ids, tmp_ids, sizeof(int) * all_available_cube_count);
- *n_cube = all_available_cube_count;
-
- free(tmp_ids);
+ cube_manager_list(instance->cube_manager, cube_ids, n_cube);
}
-int fieldstat_get_metrics_used_by_cube(const struct fieldstat *instance, int cube_id, int **metric_id_out, size_t *n_metric)
+struct field_list *fieldstat_cube_get_dimensions(const struct fieldstat *instance, int cube_id)
{
- *metric_id_out = NULL;
- *n_metric = 0;
- if (instance == NULL) {
- return FS_ERR_NULL_HANDLER;
- }
- if (cube_id >= instance->valid_cube_arr_length || cube_id < 0) {
- return FS_ERR_INVALID_CUBE_ID;
- }
- const struct fs_cube *cube = instance->cube[cube_id];
+ const struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
if (cube == NULL) {
- return FS_ERR_INVALID_CUBE_ID;
- }
-
- int all_available_metric_count = 0;
- if (cube->valid_metric_arr_len == 0) {
- return FS_OK;
- }
- int *tmp_ids = (int *)malloc(sizeof(int) * cube->valid_metric_arr_len);
- for (int i = 0; i < cube->valid_metric_arr_len; i++) {
- const struct metric *tmp_metric = cube->metrics[i];
- if (tmp_metric == NULL) {
- continue;
- }
- tmp_ids[all_available_metric_count] = i;
- all_available_metric_count ++;
- }
- if (all_available_metric_count == 0) {
- free(tmp_ids);
- return FS_OK;
- }
-
- *metric_id_out = tmp_ids;
- *n_metric = all_available_metric_count;
-
- return FS_OK;
-}
-
-void fieldstat_get_metrics(const struct fieldstat *instance, int **metric_id_out, size_t *n_metric)
-{
- if (instance == NULL || instance->n_metric_master == 0) {
- *metric_id_out = NULL;
- *n_metric = 0;
- return;
- }
-
- int *tmp_ids = (int *)malloc(sizeof(int) * instance->n_metric_master);
- *metric_id_out = tmp_ids;
- *n_metric = instance->n_metric_master;
- for (int i = 0; i < instance->n_metric_master; i++) {
- tmp_ids[i] = i;
+ return NULL;
}
-
- return;
+
+ return cube_get_identifier(cube);
}
-void fieldstat_get_cells_used_by_metric(const struct fieldstat *instance, int cube_id, int metric_id,
- struct fieldstat_tag_list **tag_list, size_t *n_cell)
+int fieldstat_counter_get(const struct fieldstat *instance, int cube_id, const struct field_list *cell_dimensions, int metric_id, long long *value)
{
- *tag_list = NULL;
- *n_cell = 0;
- int ret = FS_OK;
- const struct metric *metric = fieldstat_find_metric(instance, cube_id, metric_id, &ret);
- if (metric == NULL) {
- return;
- }
-
- size_t n_cell_ret = 0;
- int *cell_ids = NULL;
- metric_get_cell_ids(metric, &cell_ids, &n_cell_ret);
- if (n_cell_ret == 0) {
- return;
- }
- *n_cell = n_cell_ret;
-
- struct fieldstat_tag_list *tag_list_ret = (struct fieldstat_tag_list *)malloc(sizeof(struct fieldstat_tag_list) * n_cell_ret);
- *tag_list = tag_list_ret;
- for (int i = 0; i < n_cell_ret; i++) {
- const struct tag_hash_key *tag_key = cell_manager_get_tag_by_cell_id(instance->cube[cube_id]->cell_manager, cell_ids[i]);
- if (tag_key == NULL) {
- continue;
- }
- tag_hash_key_convert_to_fieldstat_tag(tag_key, &(tag_list_ret[i].tag), &(tag_list_ret[i].n_tag));
+ const struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
+ return FS_ERR_INVALID_CUBE_ID;
}
-
- free(cell_ids);
+
+ return cube_counter_get(cube, metric_id, cell_dimensions, value);
}
-struct fieldstat_tag_list *fieldstat_get_shared_tags(const struct fieldstat *instance, int cube_id)
+int fieldstat_hll_get(const struct fieldstat *instance, int cube_id, const struct field_list *cell_dimensions, int metric_id, double *value)
{
- if (instance == NULL || cube_id >= instance->valid_cube_arr_length || cube_id < 0) {
- return NULL;
- }
- struct fs_cube *cube = instance->cube[cube_id];
+ const struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
if (cube == NULL) {
- return NULL;
- }
-
- struct fieldstat_tag_list *tag_list = (struct fieldstat_tag_list *)malloc(sizeof(struct fieldstat_tag_list));
-
- if (cube->n_shared_tags == 0) {
- tag_list->tag = NULL;
- tag_list->n_tag = 0;
- return tag_list;
- }
-
- tag_list->tag = (struct fieldstat_tag *)malloc(sizeof(struct fieldstat_tag) * cube->n_shared_tags);
- for (int i = 0; i < cube->n_shared_tags; i++) {
- struct fieldstat_tag *tag_dest = &(tag_list->tag[i]);
- struct fieldstat_tag *tag_src = &(cube->shared_tags[i]);
- tag_dest->key = strdup(tag_src->key);
- tag_dest->type = tag_src->type;
- switch (tag_src->type) {
- case TAG_INTEGER:
- tag_dest->value_longlong = tag_src->value_longlong;
- break;
- case TAG_CSTRING:
- tag_dest->value_str = strdup(tag_src->value_str);
- break;
- case TAG_DOUBLE:
- tag_dest->value_double = tag_src->value_double;
- break;
- default:
- break;
- }
+ return FS_ERR_INVALID_CUBE_ID;
}
- tag_list->n_tag = cube->n_shared_tags;
-
- return tag_list;
-}
+
+ int ret = cube_hll_get(cube, metric_id, cell_dimensions, value);
-int get_cell_id_by_tag_list(const struct fieldstat *instance, int cube_id, const struct fieldstat_tag_list *tags)
-{
- struct tag_hash_key tag_key;
- tag_hash_key_init_with_fieldstat_tag(&tag_key, tags->tag, tags->n_tag, false);
- int cell_id = cell_manager_find(instance->cube[cube_id]->cell_manager, &tag_key);
- return cell_id;
+ return ret;
}
-int fieldstat_counter_get(const struct fieldstat *instance, int cube_id, int metric_id, const struct fieldstat_tag_list *tags, long long *value)
+long long fieldstat_histogram_value_at_percentile(const struct fieldstat *instance, int cube_id, const struct field_list *cell_dimensions, int metric_id, double percentile)
{
- int ret;
- *value = 0;
- const struct metric *metric = fieldstat_find_metric(instance, cube_id, metric_id, &ret);
- if (ret != FS_OK) {
- return ret;
- }
-
- if (metric == NULL || metric_get_type(metric) != METRIC_TYPE_COUNTER) {
- return FS_ERR_INVALID_METRIC_ID;
- }
-
- int cell_id = get_cell_id_by_tag_list(instance, cube_id, tags);
- if (cell_id == -1) {
- return FS_ERR_INVALID_TAG;
+ const struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
+ return FS_ERR_INVALID_CUBE_ID;
}
-
- ret = metric_counter_get(metric, cell_id, value);
+ long long value;
+ int ret = cube_histogram_value_at_percentile(cube, metric_id, cell_dimensions, percentile, &value);
if (ret < 0) {
- return FS_ERR_INVALID_TAG;
- }
-
- return FS_OK;
-}
-
-int fieldstat_hll_get(const struct fieldstat *instance, int cube_id, int metric_id, const struct fieldstat_tag_list *tags, double *value)
-{
- int ret;
- const struct metric *metric = fieldstat_find_metric(instance, cube_id, metric_id, &ret);
- if (ret != FS_OK) {
return ret;
}
- if (metric == NULL || metric_get_type(metric) != METRIC_TYPE_HLL) {
- return FS_ERR_INVALID_METRIC_ID;
- }
-
- int cell_id = get_cell_id_by_tag_list(instance, cube_id, tags);
- if (cell_id == -1) {
- return FS_ERR_INVALID_TAG;
- }
-
- double ret2 = metric_hll_get(metric, cell_id);
- if (ret2 < 0) {
- return FS_ERR_INVALID_TAG;
- }
- *value = ret2;
- return FS_OK;
+ return value;
}
-long long fieldstat_hist_value_at_percentile(const struct fieldstat *instance, int cube_id, int metric_id, const struct fieldstat_tag_list *tags, double percentile)
+long long fieldstat_histogram_count_le_value(const struct fieldstat *instance, int cube_id, const struct field_list *cell_dimensions, int metric_id, long long value)
{
- int ret;
- const struct metric *metric = fieldstat_find_metric(instance, cube_id, metric_id, &ret);
- if (ret != FS_OK) {
- return ret;
- }
- if (metric == NULL || metric_get_type(metric) != METRIC_TYPE_HISTOGRAM) {
- return FS_ERR_INVALID_METRIC_ID;
- }
-
- int cell_id = get_cell_id_by_tag_list(instance, cube_id, tags);
- if (cell_id == -1) {
- return FS_ERR_INVALID_TAG;
- }
- long long ret2 = metric_histogram_value_at_percentile(metric, cell_id, percentile);
- if (ret2 < 0) {
- return FS_ERR_INVALID_TAG;
+ const struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
+ return FS_ERR_INVALID_CUBE_ID;
}
-
- return ret2;
-}
-
-long long fieldstat_hist_count_le_value(const struct fieldstat *instance, int cube_id, int metric_id, const struct fieldstat_tag_list *tags, long long value)
-{
- int ret;
- const struct metric *metric = fieldstat_find_metric(instance, cube_id, metric_id, &ret);
- if (ret != FS_OK) {
+ long long count;
+ int ret = cube_histogram_count_le_value(cube, metric_id, cell_dimensions, value, &count);
+ if (ret < 0) {
return ret;
}
- if (metric == NULL || metric_get_type(metric) != METRIC_TYPE_HISTOGRAM) {
- return FS_ERR_INVALID_METRIC_ID;
- }
-
- int cell_id = get_cell_id_by_tag_list(instance, cube_id, tags);
- if (cell_id == -1) {
- return FS_ERR_INVALID_TAG;
- }
-
- long long ret2 = metric_histogram_count_le_value(metric, cell_id, value);
- if (ret2 < 0) {
- return FS_ERR_INVALID_TAG;
- }
-
- return ret2;
+ return count;
}
-void fieldstat_get_serialized_blob(const struct fieldstat *instance, int cube_id, int metric_id, const struct fieldstat_tag_list *tags, char **blob, size_t *blob_size)
+void fieldstat_metric_get_serialization_as_base64(const struct fieldstat *instance, int cube_id, int metric_id, const struct field_list *cell_dimensions, char **blob, size_t *blob_size)
{
- *blob = NULL;
- *blob_size = 0;
- int ret;
- const struct metric *metric = fieldstat_find_metric(instance, cube_id, metric_id, &ret);
- if (ret != FS_OK) {
- return;
- }
- if (metric == NULL) {
- return;
- }
- enum metric_type type = metric_get_type(metric);
- if (!(type == METRIC_TYPE_HLL || type == METRIC_TYPE_HISTOGRAM)) {
- return;
- }
-
- int cell_id = get_cell_id_by_tag_list(instance, cube_id, tags);
- if (cell_id == -1) {
+ const struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
+ *blob = NULL;
+ *blob_size = 0;
return;
}
- metric_get_plain_blob(metric, cell_id, blob, blob_size);
+ cube_get_serialization_as_base64(cube, metric_id, cell_dimensions, blob, blob_size);
}
-void fieldstat_tag_list_arr_free(struct fieldstat_tag_list *tag_list, size_t n_cell)
+void fieldstat_field_list_arr_free(struct field_list *field_lists, size_t n_field_list)
{
- if (tag_list == NULL) {
+ if (field_lists == NULL) {
return;
}
- for (int i = 0; i < n_cell; i++) {
- fieldstat_free_tag_array(tag_list[i].tag, tag_list[i].n_tag);
+ for (int i = 0; i < n_field_list; i++) {
+ for (size_t j = 0; j < field_lists[i].n_field; j++) {
+ struct field *field = &(field_lists[i].field[j]);
+ free((char *)field->key);
+ if (field->type == FIELD_VALUE_CSTRING) {
+ free((char *)field->value_str);
+ }
+ }
+ free(field_lists[i].field);
}
- free(tag_list);
+ free(field_lists);
}
-const char *fieldstat_get_metric_name(const struct fieldstat *instance, int metric_id)
+//uninitialized
+
+const char *fieldstat_get_metric_name(const struct fieldstat *instance, int cube_id, int metric_id)
{
- if (metric_id < 0 || metric_id >= instance->n_metric_master) {
- return NULL;
- }
- const struct metric *metric = instance->metric_masters[metric_id];
- if (metric == NULL) {
+ const struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
return NULL;
}
-
- return metric_get_name(metric);
+ return cube_get_metric_name(cube, metric_id);
}
-enum metric_type fieldstat_get_metric_type(const struct fieldstat *instance, int metric_id)
+enum metric_type fieldstat_get_metric_type(const struct fieldstat *instance, int cube_id, int metric_id)
{
- if (instance == NULL || metric_id < 0 || metric_id >= instance->n_metric_master) {
- return (enum metric_type)(-1);
- }
- const struct metric *metric = instance->metric_masters[metric_id];
- if (metric == NULL) {
+ const struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ if (cube == NULL) {
return (enum metric_type)(-1);
}
-
- return metric_get_type(metric);
+ return cube_get_metric_type(cube, metric_id);
}
-void fieldstat_get_cells_used_by_cube(const struct fieldstat *instance, int cube_id, struct fieldstat_tag_list **tag_list, size_t *n_cell)
+void fieldstat_cube_get_cells(const struct fieldstat *instance, int cube_id, struct field_list **cell_dimensions, size_t *n_cell)
{
- if (instance == NULL || cube_id < 0 || cube_id >= instance->valid_cube_arr_length) {
- return;
- }
- const struct fs_cube *cube = instance->cube[cube_id];
+ const struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
if (cube == NULL) {
return;
}
- int arr_len = 0;
- const struct tag_hash_key **tags_discontinuous = cell_manager_dump(cube->cell_manager, &arr_len);
- if (arr_len == 0) {
- *tag_list = NULL;
- *n_cell = 0;
- return;
- }
-
- struct fieldstat_tag_list *tag_list_ret = (struct fieldstat_tag_list *)malloc(sizeof(struct fieldstat_tag_list) * arr_len);
- int n_cell_ret = 0;
-
- for (int i = 0; i < arr_len; i++) {
- const struct tag_hash_key *tag_key = tags_discontinuous[i];
- if (tag_key == NULL) {
- continue;
- }
- tag_hash_key_convert_to_fieldstat_tag(tag_key, &(tag_list_ret[n_cell_ret].tag), &(tag_list_ret[n_cell_ret].n_tag));
- n_cell_ret++;
- }
- if (n_cell_ret == 0) {
- free(tag_list_ret);
- *tag_list = NULL;
- *n_cell = 0;
- return;
- }
-
- *tag_list = tag_list_ret;
- *n_cell = n_cell_ret;
+ cube_get_cells(cube, cell_dimensions, n_cell);
}
int fieldstat_get_used_sampling(const struct fieldstat *instance, int cube_id)
@@ -1417,52 +354,36 @@ int fieldstat_get_used_sampling(const struct fieldstat *instance, int cube_id)
if (instance == NULL) {
return FS_ERR_NULL_HANDLER;
}
- if (cube_id < 0 || cube_id >= instance->valid_cube_arr_length) {
- return FS_ERR_INVALID_CUBE_ID;
- }
- const struct fs_cube *cube = instance->cube[cube_id];
+
+ const struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
if (cube == NULL) {
return FS_ERR_INVALID_CUBE_ID;
}
- return cell_manager_get_cardinality(cube->cell_manager);
+ return cube_get_cell_count(cube);
}
-int fieldstat_find_cube(const struct fieldstat *instance, const struct fieldstat_tag *shared_tags, size_t n_shared_tags)
+int fieldstat_find_cube(const struct fieldstat *instance, const struct field *cube_dimensions, size_t n_dimensions)
{
if (instance == NULL) {
return FS_ERR_NULL_HANDLER;
}
- const struct cube_manager *tag_cube_id_map = instance->shared_tag_cube_manager;
- struct tag_hash_key shared_tag_key;
- tag_hash_key_init_with_fieldstat_tag(&shared_tag_key, shared_tags, n_shared_tags, false);
- int cube_id = cube_manager_find(tag_cube_id_map, &shared_tag_key);
+
+ int cube_id = cube_manager_find(instance->cube_manager, cube_dimensions, n_dimensions);
if (cube_id == -1) {
- return FS_ERR_INVALID_KEY;
+ return FS_ERR_INVALID_DIMENSION;
}
return cube_id;
}
-int fieldstat_get_cube_mode(const struct fieldstat *instance, int cube_id, enum sampling_mode *mode, int *primary_metric_id)
+void fieldstat_get_metric_in_cell(const struct fieldstat *instance, int cube_id, const struct field_list *cell_dimensions, int **metric_id_out, size_t *n_metric_out)
{
- if (instance == NULL) {
- return FS_ERR_NULL_HANDLER;
- }
- if (cube_id < 0 || cube_id >= instance->valid_cube_arr_length) {
- return FS_ERR_INVALID_CUBE_ID;
- }
- const struct fs_cube *cube = instance->cube[cube_id];
- if (cube == NULL) {
- return FS_ERR_INVALID_CUBE_ID;
- }
-
- *mode = cube->sampling_mode;
- if (cube->sampling_mode == SAMPLING_MODE_TOPK) {
- *primary_metric_id = cube->primary_metric_id;
- } else {
- *primary_metric_id = -1;
- }
+ const struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ return cube_get_metrics_in_cell(cube, cell_dimensions, metric_id_out, n_metric_out);
+}
- return FS_OK;
-} \ No newline at end of file
+void fieldstat_cube_get_metrics(const struct fieldstat *instance, int cube_id, int **metric_id_out, size_t *n_metric) {
+ const struct cube *cube = cube_manager_get_cube_by_id(instance->cube_manager, cube_id);
+ cube_get_metrics(cube, metric_id_out, n_metric);
+}
diff --git a/src/fieldstat_easy.c b/src/fieldstat_easy.c
index 9319a41..f32dbd9 100644
--- a/src/fieldstat_easy.c
+++ b/src/fieldstat_easy.c
@@ -114,7 +114,7 @@ void *fs_easy_output_thread(void *arg) // return void * for pthread_create check
return NULL; // return void * for pthread_create check only
}
-struct fieldstat_easy *fieldstat_easy_new(int max_thread_num, const char *name, const struct fieldstat_tag *tags, size_t n_tag) {
+struct fieldstat_easy *fieldstat_easy_new(int max_thread_num, const char *name, const struct field *global_dimensions, size_t n_dimension) {
if (max_thread_num <= 0) {
return NULL;
}
@@ -122,12 +122,13 @@ struct fieldstat_easy *fieldstat_easy_new(int max_thread_num, const char *name,
fse->fsu = malloc(sizeof(struct fs_easy_thread) * max_thread_num);
fse->max_thread_num = max_thread_num;
fse->delta = fieldstat_new();
- fieldstat_create_cube(fse->delta, NULL, 0, SAMPLING_MODE_COMPREHENSIVE, 0);
+ fieldstat_cube_create(fse->delta, NULL, 0);
+ fieldstat_cube_set_sampling(fse->delta, 0, SAMPLING_MODE_COMPREHENSIVE, 0, 0);
fse->accumulate = fieldstat_fork(fse->delta);
fse->exporter = fieldstat_json_exporter_new();
- if (tags != NULL && n_tag > 0) {
- fieldstat_json_exporter_set_global_tag(fse->exporter, tags, n_tag);
+ if (global_dimensions != NULL && n_dimension > 0) {
+ fieldstat_json_exporter_set_global_tag(fse->exporter, global_dimensions, n_dimension);
}
if (name != NULL) {
fieldstat_json_exporter_set_name(fse->exporter, name);
@@ -192,17 +193,17 @@ int fieldstat_easy_register_counter(struct fieldstat_easy *fse, const char *name
pthread_spin_lock(&fse->fsu[i].lock);
}
- int ret = fieldstat_register_counter(fse->fsu[0].active, name); // try to register
+ int ret = fieldstat_register_counter(fse->fsu[0].active, 0, name); // try to register
if (ret < 0) {
for (int i = 0; i < fse->max_thread_num; i++) {
pthread_spin_unlock(&fse->fsu[i].lock);
}
return ret;
}
- fieldstat_register_counter(fse->fsu[0].read_only, name);
+ fieldstat_register_counter(fse->fsu[0].read_only, 0, name);
for (int i = 1; i < fse->max_thread_num; i++) {
- fieldstat_register_counter(fse->fsu[i].active, name);
- fieldstat_register_counter(fse->fsu[i].read_only, name);
+ fieldstat_register_counter(fse->fsu[i].active, 0, name);
+ fieldstat_register_counter(fse->fsu[i].read_only, 0, name);
}
for (int i = 0; i < fse->max_thread_num; i++) {
@@ -218,17 +219,17 @@ int fieldstat_easy_register_histogram(struct fieldstat_easy *fse, const char *na
pthread_spin_lock(&fse->fsu[i].lock);
}
- int ret = fieldstat_register_hist(fse->fsu[0].active, name, lowest_trackable_value, highest_trackable_value, significant_figures); // try to register
+ int ret = fieldstat_register_histogram(fse->fsu[0].active, 0, name, lowest_trackable_value, highest_trackable_value, significant_figures); // try to register
if (ret < 0) {
for (int i = 0; i < fse->max_thread_num; i++) {
pthread_spin_unlock(&fse->fsu[i].lock);
}
return ret;
}
- fieldstat_register_hist(fse->fsu[0].read_only, name, lowest_trackable_value, highest_trackable_value, significant_figures);
+ fieldstat_register_histogram(fse->fsu[0].read_only, 0, name, lowest_trackable_value, highest_trackable_value, significant_figures);
for (int i = 1; i < fse->max_thread_num; i++) {
- fieldstat_register_hist(fse->fsu[i].active, name, lowest_trackable_value, highest_trackable_value, significant_figures);
- fieldstat_register_hist(fse->fsu[i].read_only, name, lowest_trackable_value, highest_trackable_value, significant_figures);
+ fieldstat_register_histogram(fse->fsu[i].active, 0, name, lowest_trackable_value, highest_trackable_value, significant_figures);
+ fieldstat_register_histogram(fse->fsu[i].read_only, 0, name, lowest_trackable_value, highest_trackable_value, significant_figures);
}
for (int i = 0; i < fse->max_thread_num; i++) {
@@ -250,35 +251,35 @@ struct timeval get_current_timestamp()
struct fieldstat *merge_all_instance(struct fieldstat_easy *fse)
{
- struct fieldstat *dst = fieldstat_new();
+ struct fieldstat *dest = fieldstat_new();
// collect all the data recorded since last passive output, if passive output happened. Otherwise, its the data since the program started.
for (int i = 0; i < fse->max_thread_num; i++) {
pthread_spin_lock(&fse->fsu[i].lock);
- fieldstat_merge(dst, fse->fsu[i].active);
+ fieldstat_merge(dest, fse->fsu[i].active);
pthread_spin_unlock(&fse->fsu[i].lock);
}
// add the outputted data
if (fse->output_thread_running) {
pthread_spin_lock(&fse->outputting_lock);
- fieldstat_merge(dst, fse->accumulate);
+ fieldstat_merge(dest, fse->accumulate);
pthread_spin_unlock(&fse->outputting_lock);
}
- return dst;
+ return dest;
}
// output an json string for accumulated data
void fieldstat_easy_output(struct fieldstat_easy *fse, char **buff, size_t *buff_len)
{
- struct fieldstat *dst = merge_all_instance(fse);
+ struct fieldstat *dest = merge_all_instance(fse);
struct timeval timestamp = get_current_timestamp();
- *buff = fieldstat_json_exporter_export(fse->exporter, dst, &timestamp);
+ *buff = fieldstat_json_exporter_export_flat(fse->exporter, dest, &timestamp);
if (*buff == NULL) {
*buff = strdup("[]");
}
- fieldstat_free(dst);
+ fieldstat_free(dest);
*buff_len = strlen(*buff);
}
@@ -286,10 +287,10 @@ void fieldstat_easy_output(struct fieldstat_easy *fse, char **buff, size_t *buff
void fieldstat_easy_output_array(struct fieldstat_easy *fse, char ***json_objects, size_t *n_object)
{
struct timeval timestamp = get_current_timestamp();
- struct fieldstat *dst = merge_all_instance(fse);
+ struct fieldstat *dest = merge_all_instance(fse);
- fieldstat_json_exporter_export_array(fse->exporter, dst, &timestamp, json_objects, n_object);
- fieldstat_free(dst);
+ fieldstat_json_exporter_export_flat_array(fse->exporter, dest, &timestamp, json_objects, n_object);
+ fieldstat_free(dest);
}
int fieldstat_easy_output_array_and_reset(struct fieldstat_easy *fse, char ***json_objects, size_t *n_object)
@@ -299,20 +300,20 @@ int fieldstat_easy_output_array_and_reset(struct fieldstat_easy *fse, char ***js
return -1;
}
- struct fieldstat *dst = fieldstat_new();
+ struct fieldstat *dest = fieldstat_new();
for (int i = 0; i < fse->max_thread_num; i++) {
rcu_reclaim_handler(fse->fsu + i);
- fieldstat_merge(dst, fse->fsu[i].read_only);
+ fieldstat_merge(dest, fse->fsu[i].read_only);
}
struct timeval timestamp = get_current_timestamp();
- fieldstat_json_exporter_export_array(fse->exporter, dst, &timestamp, json_objects, n_object);
- fieldstat_free(dst);
+ fieldstat_json_exporter_export_flat_array(fse->exporter, dest, &timestamp, json_objects, n_object);
+ fieldstat_free(dest);
return 0;
}
-int fieldstat_easy_counter_incrby(struct fieldstat_easy *fse, int thread_id, int metric_id, const struct fieldstat_tag *tags, size_t n_tag, long long increment)
+int fieldstat_easy_counter_incrby(struct fieldstat_easy *fse, int thread_id, int metric_id, const struct field *dimensions, size_t n_dimensions, long long increment)
{
if (thread_id < 0) {
return -1;
@@ -322,13 +323,13 @@ int fieldstat_easy_counter_incrby(struct fieldstat_easy *fse, int thread_id, int
}
pthread_spin_lock(&fse->fsu[thread_id].lock);
- int ret = fieldstat_counter_incrby(fse->fsu[thread_id].active, 0, metric_id, tags, n_tag, increment);
+ int ret = fieldstat_counter_incrby(fse->fsu[thread_id].active, 0, metric_id, dimensions, n_dimensions, increment);
pthread_spin_unlock(&fse->fsu[thread_id].lock);
return ret;
}
-int fieldstat_easy_counter_set(struct fieldstat_easy *fse, int thread_id, int metric_id, const struct fieldstat_tag *tags, size_t n_tag, long long value)
+int fieldstat_easy_counter_set(struct fieldstat_easy *fse, int thread_id, int metric_id, const struct field *dimensions, size_t n_dimensions, long long value)
{
if (thread_id < 0) {
return -1;
@@ -338,13 +339,13 @@ int fieldstat_easy_counter_set(struct fieldstat_easy *fse, int thread_id, int me
}
pthread_spin_lock(&fse->fsu[thread_id].lock);
- int ret = fieldstat_counter_set(fse->fsu[thread_id].active, 0, metric_id, tags, n_tag, value);
+ int ret = fieldstat_counter_set(fse->fsu[thread_id].active, 0, metric_id, dimensions, n_dimensions, value);
pthread_spin_unlock(&fse->fsu[thread_id].lock);
return ret;
}
-int fieldstat_easy_histogram_record(struct fieldstat_easy *fse, int thread_id, int metric_id, const struct fieldstat_tag *tags, size_t n_tag, long long value)
+int fieldstat_easy_histogram_record(struct fieldstat_easy *fse, int thread_id, int metric_id, const struct field *dimensions, size_t n_dimensions, long long value)
{
if (thread_id < 0) {
return -1;
@@ -354,13 +355,8 @@ int fieldstat_easy_histogram_record(struct fieldstat_easy *fse, int thread_id, i
}
pthread_spin_lock(&fse->fsu[thread_id].lock);
- int ret = fieldstat_hist_record(fse->fsu[thread_id].active, 0, metric_id, tags, n_tag, value);
+ int ret = fieldstat_histogram_record(fse->fsu[thread_id].active, 0, metric_id, dimensions, n_dimensions, value);
pthread_spin_unlock(&fse->fsu[thread_id].lock);
return ret;
}
-
-void fieldstat_easy_enable_delta_in_active_output(struct fieldstat_easy *fse)
-{
- fieldstat_json_exporter_enable_delta(fse->exporter);
-} \ No newline at end of file
diff --git a/src/metrics/st_hyperloglog.c b/src/metrics/hyperloglog.c
index 5085671..ea05887 100644
--- a/src/metrics/st_hyperloglog.c
+++ b/src/metrics/hyperloglog.c
@@ -3,7 +3,7 @@
* For our needs, we always use a dense representation and avoid the sparse/dense conversions.
*
*/
-#include "st_hyperloglog.h"
+#include "hyperloglog.h"
#include "xxhash/xxhash.h"
#include "crdt_utils.h"
@@ -14,16 +14,18 @@
#include <stdlib.h>
#include "base64/b64.h"
-const size_t BLOB_HDR_SIZE= offsetof(struct ST_hyperloglog, registers);
+#include "hll_common.h"
+const size_t BLOB_HDR_SIZE= offsetof(struct hyperloglog, registers);
-struct ST_hyperloglog *ST_hyperloglog_new(unsigned char precision)
+
+struct hyperloglog *hyperloglog_new(unsigned char precision)
{
// Ensure the precision is somewhat sane
if (precision < HLL_MIN_PRECISION || precision > HLL_MAX_PRECISION)
return NULL;
- struct ST_hyperloglog *h=ALLOC(struct ST_hyperloglog, 1);
+ struct hyperloglog *h=ALLOC(struct hyperloglog, 1);
h->cfg.precision = precision;
@@ -37,24 +39,8 @@ struct ST_hyperloglog *ST_hyperloglog_new(unsigned char precision)
h->registers = ALLOC(uint32_t, words);
return h;
}
-void ST_hyperloglog_configure(struct ST_hyperloglog *h, unsigned char precision, int time_window_seconds, const struct timeval now)
-{
- if(h->cfg.precision != precision)
- {
- free(h->registers);
- // Determine how many registers are needed
- int reg = NUM_REG(precision);
-
- // Get the full words required
- int words = INT_CEIL(reg, REG_PER_WORD);
- // Allocate and zero out the registers
- h->registers = ALLOC(uint32_t, words);
- h->cfg.precision=precision;
- }
- return;
-}
-void ST_hyperloglog_free(struct ST_hyperloglog *h)
+void hyperloglog_free(struct hyperloglog *h)
{
free(h->registers);
h->registers=NULL;
@@ -62,14 +48,14 @@ void ST_hyperloglog_free(struct ST_hyperloglog *h)
return;
}
-static int get_register(const struct ST_hyperloglog *h, int idx) {
+static int get_register(const struct hyperloglog *h, int idx) {
uint32_t word = *(h->registers + (idx / REG_PER_WORD));
word = word >> REG_WIDTH * (idx % REG_PER_WORD);
return word & ((1 << REG_WIDTH) - 1);
}
-static void set_register(const struct ST_hyperloglog *h, int idx, int val) {
+static void set_register(const struct hyperloglog *h, int idx, int val) {
uint32_t *word = h->registers + (idx / REG_PER_WORD);
// Shift the val into place
@@ -82,7 +68,7 @@ static void set_register(const struct ST_hyperloglog *h, int idx, int val) {
return;
}
-static void reset_register(const struct ST_hyperloglog *h, int idx)
+static void reset_register(const struct hyperloglog *h, int idx)
{
uint32_t *word = h->registers + (idx / REG_PER_WORD);
unsigned shift = REG_WIDTH * (idx % REG_PER_WORD);
@@ -90,7 +76,7 @@ static void reset_register(const struct ST_hyperloglog *h, int idx)
*word &= ~val_mask;
}
-void ST_hyperloglog_reset(struct ST_hyperloglog *h)
+void hyperloglog_reset(struct hyperloglog *h)
{
int n_register=NUM_REG(h->cfg.precision);
@@ -100,7 +86,7 @@ void ST_hyperloglog_reset(struct ST_hyperloglog *h)
return;
}
-int hll_add_hash(struct ST_hyperloglog *h, uint64_t hash)
+int hyperloglog_add_hash(struct hyperloglog *h, uint64_t hash)
{
// Determine the index using the first p bits
int idx = hash >> (64 - h->cfg.precision);
@@ -119,30 +105,30 @@ int hll_add_hash(struct ST_hyperloglog *h, uint64_t hash)
return 0;
}
-int ST_hyperloglog_add(struct ST_hyperloglog *h, const char *key, size_t keylen)
+int hyperloglog_add(struct hyperloglog *h, const char *key, size_t keylen)
{
uint64_t hash=0;
hash=XXH3_64bits_withSeed(key, keylen, 171);
// Add the hashed value
- return hll_add_hash(h, hash);
+ return hyperloglog_add_hash(h, hash);
}
// https://djhworld.github.io/hyperloglog/merging/
-int ST_hyperloglog_merge(struct ST_hyperloglog *dst, const struct ST_hyperloglog *src)
+int hyperloglog_merge(struct hyperloglog *dest, const struct hyperloglog *src)
{
- if(dst->cfg.precision != src->cfg.precision) return -1;
- int n_register=NUM_REG(dst->cfg.precision);
+ if(dest->cfg.precision != src->cfg.precision) return -1;
+ int n_register=NUM_REG(dest->cfg.precision);
int s_reg=0, d_reg=0;
for(int i=0; i<n_register; i++)
{
s_reg=get_register(src, i);
- d_reg=get_register(dst, i);
- set_register(dst, i, MAX(s_reg, d_reg));
+ d_reg=get_register(dest, i);
+ set_register(dest, i, MAX(s_reg, d_reg));
}
return 0;
}
-size_t ST_hyperloglog_serialized_size(const struct ST_hyperloglog *h)
+size_t hyperloglog_serialized_size(const struct hyperloglog *h)
{
size_t sz=0;
size_t num_reg = NUM_REG(h->cfg.precision);
@@ -153,13 +139,13 @@ size_t ST_hyperloglog_serialized_size(const struct ST_hyperloglog *h)
return sz;
}
-void ST_hyperloglog_serialize(const struct ST_hyperloglog *h, char **blob, size_t *blob_sz)
+void hyperloglog_serialize(const struct hyperloglog *h, char **blob, size_t *blob_sz)
{
size_t sz=0, offset=0;
size_t num_reg = NUM_REG(h->cfg.precision);
size_t words = INT_CEIL(num_reg, REG_PER_WORD);
- sz = ST_hyperloglog_serialized_size(h);
+ sz = hyperloglog_serialized_size(h);
char *buffer = ALLOC(char, sz);
memcpy(buffer+offset, h, BLOB_HDR_SIZE);
@@ -172,9 +158,9 @@ void ST_hyperloglog_serialize(const struct ST_hyperloglog *h, char **blob, size_
return;
}
-struct ST_hyperloglog *ST_hyperloglog_deserialize(const char *blob, size_t blob_sz)
+struct hyperloglog *hyperloglog_deserialize(const char *blob, size_t blob_sz)
{
- struct ST_hyperloglog *h=ALLOC(struct ST_hyperloglog, 1);
+ struct hyperloglog *h=ALLOC(struct hyperloglog, 1);
size_t offset=0;
memcpy(h, blob, BLOB_HDR_SIZE);
offset += BLOB_HDR_SIZE;
@@ -187,43 +173,16 @@ struct ST_hyperloglog *ST_hyperloglog_deserialize(const char *blob, size_t blob_
return h;
}
-void ST_hyperloglog_serialize_for_networking(const struct ST_hyperloglog *h, char **blob, size_t *blob_sz)
+void hyperloglog_serialize_into_base64(const struct hyperloglog *h, char **blob, size_t *blob_sz)
{
- size_t sz=0, offset=0;
- size_t num_reg = NUM_REG(h->cfg.precision);
- size_t words = INT_CEIL(num_reg, REG_PER_WORD);
-
- sz = ST_hyperloglog_serialized_size(h) + 1; // [precision][version][data...
- unsigned char *buffer = ALLOC(unsigned char, sz);
-
- const unsigned char version = 1;
- memcpy(buffer+offset, &version, 1);
- offset += 1;
-
- unsigned char precision = h->cfg.precision;
- memcpy(buffer+offset, &precision, 1);
- offset += 1;
-
- for (int i = 0; i < words; i++) {
- uint32_t word = h->registers[i];
- buffer[offset++] = word >> 24;
- buffer[offset++] = word >> 16;
- buffer[offset++] = word >> 8;
- buffer[offset++] = word;
- }
-
- char *enc = b64_encode(buffer, sz);
- free(buffer);
- *blob_sz = strlen(enc);
- *blob = enc;
- return;
+ hll_encode_into_base64(h->registers, h->cfg.precision, blob, blob_sz);
}
-void ST_hyperloglog_merge_blob(struct ST_hyperloglog *dst, const char *blob, size_t blob_sz)
+void hyperloglog_merge_blob(struct hyperloglog *dest, const char *blob, size_t blob_sz)
{
- struct ST_hyperloglog *src=ST_hyperloglog_deserialize(blob, blob_sz);
- ST_hyperloglog_merge(dst, src);
- ST_hyperloglog_free(src);
+ struct hyperloglog *src=hyperloglog_deserialize(blob, blob_sz);
+ hyperloglog_merge(dest, src);
+ hyperloglog_free(src);
return;
}
double g_switchThreshold[15] = {10, 20, 40, 80, 220, 400, 900, 1800, 3100, 6500,
@@ -314,7 +273,7 @@ static double alpha(unsigned char precision) {
/*
* Computes the raw cardinality estimate
*/
-static double raw_estimate(const struct ST_hyperloglog *h, int *num_zero)
+static double raw_estimate(const struct hyperloglog *h, int *num_zero)
{
unsigned char precision = h->cfg.precision;
int num_reg = NUM_REG(precision);
@@ -334,7 +293,7 @@ static double raw_estimate(const struct ST_hyperloglog *h, int *num_zero)
* Estimates cardinality using a linear counting.
* Used when some registers still have a zero value.
*/
-static double linear_count(const struct ST_hyperloglog *h, int num_zero)
+static double linear_count(const struct hyperloglog *h, int num_zero)
{
int registers = NUM_REG(h->cfg.precision);
return registers *
@@ -364,7 +323,7 @@ static int binary_search(double val, int num, const double *array) {
* empircal data collected by Google, from the
* paper mentioned above.
*/
-static double bias_estimate(const struct ST_hyperloglog *h, double raw_est) {
+static double bias_estimate(const struct hyperloglog *h, double raw_est) {
// Determine the samples available
int samples;
int precision = h->cfg.precision;
@@ -393,8 +352,8 @@ static double bias_estimate(const struct ST_hyperloglog *h, double raw_est) {
else
return (biases[idx] + biases[idx-1]) / 2;
}
- __attribute__ ((unused)) static double ST_HLL_count_no_sliding(const struct ST_hyperloglog *h)
-{
+
+double hyperloglog_count(const struct hyperloglog *h){
int num_zero = 0;
int num_reg = NUM_REG(h->cfg.precision);
double raw_est = raw_estimate(h, &num_zero);
@@ -420,27 +379,6 @@ static double bias_estimate(const struct ST_hyperloglog *h, double raw_est) {
return raw_est;
}
}
-double ST_hyperloglog_count(const struct ST_hyperloglog *h)
-{
- int num_zero=0;
- double raw_est=raw_estimate(h, &num_zero);
- raw_est*=alpha(h->cfg.precision);
-
- double num_reg=NUM_REG(h->cfg.precision);
- if (raw_est <= 5 * num_reg) {
- raw_est -= bias_estimate(h, raw_est);
- }
- if(raw_est<=5*num_reg/2)
- {
- if(num_zero)
- return linear_count(h, num_zero);
- }
- if(raw_est>INT32_MAX/30)
- {
- return INT32_MIN*log(1-raw_est/INT32_MAX);
- }
- return raw_est;
-}
/**
* Computes the upper bound on variance given
@@ -449,7 +387,7 @@ double ST_hyperloglog_count(const struct ST_hyperloglog *h)
* @return The expected variance in the count,
* or zero on error.
*/
-double ST_hyperloglog_error_for_precision(unsigned char precision)
+double hyperloglog_error_for_precision(unsigned char precision)
{
// Check that the error bound is sane
if (precision < HLL_MIN_PRECISION || precision > HLL_MAX_PRECISION)
diff --git a/src/metrics/st_hyperloglog.h b/src/metrics/hyperloglog.h
index 98dd7a3..b1b2330 100644
--- a/src/metrics/st_hyperloglog.h
+++ b/src/metrics/hyperloglog.h
@@ -30,30 +30,30 @@ extern "C"
#define REGISTER_SIZE(precision) INT_WIDTH*INT_CEIL(NUM_REG(precision), REG_PER_WORD)
-struct ST_HLL_configuration
+struct HLL_configuration
{
unsigned char precision;
};
-struct ST_hyperloglog
+struct hyperloglog
{
- struct ST_HLL_configuration cfg;
+ struct HLL_configuration cfg;
uint32_t *registers;
};
-struct ST_hyperloglog *ST_hyperloglog_new(unsigned char precision);
-void ST_hyperloglog_free(struct ST_hyperloglog *h);
+struct hyperloglog *hyperloglog_new(unsigned char precision);
+void hyperloglog_free(struct hyperloglog *h);
//Return 1 if at least 1 ST HyperLogLog internal register was altered. 0 otherwise.
-int ST_hyperloglog_add(struct ST_hyperloglog *h, const char *key, size_t keylen);
-void ST_hyperloglog_reset(struct ST_hyperloglog *h);
-double ST_hyperloglog_count(const struct ST_hyperloglog *h);
-size_t ST_hyperloglog_serialized_size(const struct ST_hyperloglog *h);
-void ST_hyperloglog_serialize(const struct ST_hyperloglog *h, char **blob, size_t *blob_sz);
-void ST_hyperloglog_serialize_for_networking(const struct ST_hyperloglog *h, char **blob, size_t *blob_sz);
-struct ST_hyperloglog *ST_hyperloglog_deserialize(const char *blob, size_t blob_sz);
-int ST_hyperloglog_merge(struct ST_hyperloglog *dst, const struct ST_hyperloglog *src);
-void ST_hyperloglog_merge_blob(struct ST_hyperloglog *dst, const char *blob, size_t blob_sz);
-double ST_hyperloglog_error_for_precision(unsigned char precision);
-void ST_hyperloglog_configure(struct ST_hyperloglog *h, unsigned char precision, int time_window_seconds, const struct timeval now);
+int hyperloglog_add(struct hyperloglog *h, const char *key, size_t keylen);
+int hyperloglog_add_hash(struct hyperloglog *h, uint64_t hash);
+void hyperloglog_reset(struct hyperloglog *h);
+double hyperloglog_count(const struct hyperloglog *h);
+size_t hyperloglog_serialized_size(const struct hyperloglog *h);
+void hyperloglog_serialize(const struct hyperloglog *h, char **blob, size_t *blob_sz);
+void hyperloglog_serialize_into_base64(const struct hyperloglog *h, char **blob, size_t *blob_sz);
+struct hyperloglog *hyperloglog_deserialize(const char *blob, size_t blob_sz);
+int hyperloglog_merge(struct hyperloglog *dest, const struct hyperloglog *src);
+void hyperloglog_merge_blob(struct hyperloglog *dest, const char *blob, size_t blob_sz);
+double hyperloglog_error_for_precision(unsigned char precision);
#ifdef __cplusplus
}
#endif \ No newline at end of file
diff --git a/src/metrics/metric.c b/src/metrics/metric.c
index fc7ba28..95dc646 100644
--- a/src/metrics/metric.c
+++ b/src/metrics/metric.c
@@ -3,138 +3,73 @@
#include <stdlib.h>
#include <assert.h>
#include <string.h>
-#include <stdarg.h>
#include <limits.h>
-
-#include "uthash.h"
#include "histogram_encoder.h"
#include "serializer.h"
#include "metric.h"
-#include "fieldstat.h"
-#include "tags/cell_manager.h"
#include "hdr/hdr_histogram.h"
#include "hdr/hdr_histogram_log.h"
-#include "st_hyperloglog.h"
-
-#define MAX_N_DATA_ARRAY_ITEM_INIT 1024
+#include "hyperloglog.h"
+#include "metric_manifest.h"
struct metric_counter_or_gauge {
long long value;
};
-struct para_hdr{
- long long lowest_trackable_value;
- long long highest_trackable_value;
- int significant_figures;
-};
-
-struct para_hll {
- char precision;
-};
-
-struct metric_parameter {
- union {
- struct para_hdr hdr;
- struct para_hll hll;
- };
-};
-
-struct metric_info {
- int id;
- char *name;
- struct metric_parameter *paras;
- enum metric_type type;
-};
-
-typedef struct metric_measure_data *(*metric_func_new)(const struct metric_parameter *);
-typedef void (*metric_func_del)(struct metric_measure_data *);
-typedef int (*metric_func_merge)(struct metric_measure_data *, const struct metric_measure_data *);
-typedef int (*metric_func_copy)(struct metric_measure_data *, const struct metric_measure_data *);
-typedef void (*metric_func_serialize)(const struct metric_measure_data *, char **/*blob*/, size_t */*blob_size*/);
-typedef struct metric_measure_data * (*metric_func_deserialize)(const char *, size_t );
+typedef struct metric_data *(*metric_func_new)(const union metric_parameter *);
+typedef void (*metric_func_free)(struct metric_data *);
+typedef int (*metric_func_merge)(struct metric_data *, const struct metric_data *);
+typedef struct metric_data * (*metric_func_copy)(const struct metric_data *);
+typedef void (*metric_func_serialize)(const struct metric_data *, char **/*blob*/, size_t */*blob_size*/);
+// typedef struct metric_data * (*metric_func_deserialize)(const char *, size_t );
+typedef void (*metric_func_reset)(struct metric_data *);
struct metric_scheme {
metric_func_new new;
- metric_func_del del;
+ metric_func_free free;
metric_func_merge merge;
metric_func_copy copy;
metric_func_serialize serialize;
- metric_func_deserialize deserialize;
+ // metric_func_deserialize deserialize;
+ metric_func_reset reset;
};
-struct metric_measure_data {
+struct metric_data {
union {
struct metric_counter_or_gauge *counter;
- struct ST_hyperloglog *hll;
+ struct hyperloglog *hll;
struct hdr_histogram *hdr;
};
};
struct metric {
- const struct metric_scheme *scheme;
- struct metric_info *info;
- struct metric_measure_data **data_array;
- size_t max_n_array_item;
- size_t n_array_item;
+ enum metric_type type;
+ struct metric_data *data;
+ bool operated_after_reset;
};
-
-/* -------------------------------------------------------------------------- */
-/* id cell map */
-/* -------------------------------------------------------------------------- */
-static inline struct metric_measure_data *metric_find_one_cell(const struct metric *pthis, int id) {
- if (id >= pthis->max_n_array_item) {
- return NULL;
- }
- return pthis->data_array[id];
-}
-
-void metric_add_one_cell(struct metric *pthis, int id, struct metric_measure_data *data) {
- if (id >= pthis->max_n_array_item) {
- while (id >= pthis->max_n_array_item) {
- pthis->max_n_array_item *= 2;
- }
- pthis->data_array = (struct metric_measure_data **)realloc(pthis->data_array, pthis->max_n_array_item * sizeof(struct metric_measure_data *));
- memset(pthis->data_array + pthis->n_array_item, 0, (pthis->max_n_array_item - pthis->n_array_item) * sizeof(struct metric_measure_data *));
- }
- assert(pthis->data_array[id] == NULL);
- pthis->data_array[id] = data;
-
- if (id + 1 > pthis->n_array_item) {
- pthis->n_array_item = id + 1;
- }
- return;
-}
-
/* -------------------------------------------------------------------------- */
/* scheme */
/* -------------------------------------------------------------------------- */
/* --------------------------------- counter -------------------------------- */
-struct metric_measure_data *metric_scheme_counter_new(const struct metric_parameter *_para)
+struct metric_data *metric_scheme_counter_new(const union metric_parameter *_para)
{
- struct metric_measure_data *data = (struct metric_measure_data *)malloc(sizeof(struct metric_measure_data));
+ struct metric_data *data = (struct metric_data *)malloc(sizeof(struct metric_data));
struct metric_counter_or_gauge *counter = (struct metric_counter_or_gauge *)malloc(sizeof(struct metric_counter_or_gauge));
data->counter = counter;
counter->value = 0;
return data;
}
-static void metric_scheme_counter_free(struct metric_measure_data *data)
+static void metric_scheme_counter_free(struct metric_data *data)
{
free(data->counter);
free(data);
}
-/*
- "counter": {
- "value": 0
- "mode": 0(sum) / 1(max) / 2(min)
- }
-value: long long | mode: uint32
-*/
-static void metric_scheme_counter_serialize(const struct metric_measure_data *data, char **blob, size_t *blob_size)
+static void metric_scheme_counter_serialize(const struct metric_data *data, char **blob, size_t *blob_size)
{
const struct metric_counter_or_gauge *counter = data->counter;
struct fs_reader *reader = fs_reader_new();
@@ -144,7 +79,7 @@ static void metric_scheme_counter_serialize(const struct metric_measure_data *da
fs_reader_finalize(reader, blob, blob_size);
}
-static int metric_scheme_counter_merge(struct metric_measure_data *pthis, const struct metric_measure_data *from)
+static int metric_scheme_counter_merge(struct metric_data *pthis, const struct metric_data *from)
{
struct metric_counter_or_gauge *counter = pthis->counter;
const struct metric_counter_or_gauge *from_counter = from->counter;
@@ -153,18 +88,20 @@ static int metric_scheme_counter_merge(struct metric_measure_data *pthis, const
return 0;
}
-static int metric_scheme_counter_copy(struct metric_measure_data *pthis, const struct metric_measure_data *from)
+static struct metric_data *metric_scheme_counter_copy(const struct metric_data *from)
{
- struct metric_counter_or_gauge *counter = pthis->counter;
- const struct metric_counter_or_gauge *from_counter = from->counter;
- counter->value = from_counter->value;
- return 0;
+ struct metric_data *pthis = (struct metric_data *)malloc(sizeof(struct metric_data));
+ struct metric_counter_or_gauge *counter = (struct metric_counter_or_gauge *)malloc(sizeof(struct metric_counter_or_gauge));
+ pthis->counter = counter;
+
+ counter->value = from->counter->value;
+ return pthis;
}
-struct metric_measure_data *metric_scheme_counter_deserialize(const char *blob, size_t blob_size)
+struct metric_data *metric_scheme_counter_deserialize(const char *blob, size_t blob_size)
{
struct fs_writer *writer = fs_writer_new(blob, blob_size);
- struct metric_measure_data *ret = (struct metric_measure_data *)malloc(sizeof(struct metric_measure_data));
+ struct metric_data *ret = (struct metric_data *)malloc(sizeof(struct metric_data));
struct metric_counter_or_gauge *counter = (struct metric_counter_or_gauge *)malloc(sizeof(struct metric_counter_or_gauge));
ret->counter = counter;
@@ -174,171 +111,158 @@ struct metric_measure_data *metric_scheme_counter_deserialize(const char *blob,
return ret;
}
+void metric_scheme_counter_reset(struct metric_data *data)
+{
+ data->counter->value = 0;
+}
+
/* --------------------------------- hll -------------------------------- */
-struct metric_measure_data *metric_scheme_hll_new(const struct metric_parameter *para)
+struct metric_data *metric_scheme_hll_new(const union metric_parameter *para)
{
- struct metric_measure_data *data = (struct metric_measure_data *)malloc(sizeof(struct metric_measure_data));
- struct ST_hyperloglog *hll = ST_hyperloglog_new(para->hll.precision);
+ struct metric_data *data = (struct metric_data *)malloc(sizeof(struct metric_data));
+ struct hyperloglog *hll = hyperloglog_new(para->hll.precision);
data->hll = hll;
return data;
}
-static void metric_scheme_hll_free(struct metric_measure_data *data)
+static void metric_scheme_hll_free(struct metric_data *data)
{
- ST_hyperloglog_free(data->hll);
+ hyperloglog_free(data->hll);
free(data);
}
-static void metric_scheme_hll_serialize(const struct metric_measure_data *data, char **blob, size_t *blob_size)
+static void metric_scheme_hll_serialize(const struct metric_data *data, char **blob, size_t *blob_size)
{
- ST_hyperloglog_serialize(data->hll, blob, blob_size);
+ hyperloglog_serialize(data->hll, blob, blob_size);
}
-static int metric_scheme_hll_merge(struct metric_measure_data *pthis, const struct metric_measure_data *from)
+static int metric_scheme_hll_merge(struct metric_data *pthis, const struct metric_data *from)
{
- return ST_hyperloglog_merge(pthis->hll, from->hll);
+ return hyperloglog_merge(pthis->hll, from->hll);
}
-static int metric_scheme_hll_copy(struct metric_measure_data *pthis, const struct metric_measure_data *from)
+struct metric_data *metric_scheme_hll_copy(const struct metric_data *from)
{
- return ST_hyperloglog_merge(pthis->hll, from->hll);
+ struct metric_data *pthis = (struct metric_data *)malloc(sizeof(struct metric_data));
+ struct hyperloglog *hll = hyperloglog_new(from->hll->cfg.precision);
+ pthis->hll = hll;
+ hyperloglog_merge(hll, from->hll);
+ return pthis;
}
-struct metric_measure_data *metric_scheme_hll_deserialize(const char *blob, size_t blob_size)
+struct metric_data *metric_scheme_hll_deserialize(const char *blob, size_t blob_size)
{
- struct metric_measure_data *ret = (struct metric_measure_data *)malloc(sizeof(struct metric_measure_data));
- struct ST_hyperloglog *hll = ST_hyperloglog_deserialize(blob, blob_size);
+ struct metric_data *ret = (struct metric_data *)malloc(sizeof(struct metric_data));
+ struct hyperloglog *hll = hyperloglog_deserialize(blob, blob_size);
ret->hll = hll;
return ret;
}
+void metric_scheme_hll_reset(struct metric_data *data)
+{
+ hyperloglog_reset(data->hll);
+}
/* --------------------------------- histogram -------------------------------- */
-struct metric_measure_data *metric_scheme_histogram_new(const struct metric_parameter *para)
+struct metric_data *metric_scheme_histogram_new(const union metric_parameter *para)
{
struct hdr_histogram* tmp_p_hdr = NULL;
int ret = hdr_init((int64_t)para->hdr.lowest_trackable_value, (int64_t)para->hdr.highest_trackable_value, para->hdr.significant_figures, &tmp_p_hdr);
if (ret != 0) {
return NULL;
}
- struct metric_measure_data *data = (struct metric_measure_data *)malloc(sizeof(struct metric_measure_data));
+ struct metric_data *data = (struct metric_data *)malloc(sizeof(struct metric_data));
data->hdr = tmp_p_hdr;
return data;
}
-static void metric_scheme_histogram_free(struct metric_measure_data *data)
+static void metric_scheme_histogram_free(struct metric_data *data)
{
hdr_close(data->hdr);
free(data);
}
-static void metric_scheme_histogram_serialize(const struct metric_measure_data *data, char **blob, size_t *blob_size)
+static void metric_scheme_histogram_serialize(const struct metric_data *data, char **blob, size_t *blob_size)
{
histogram_encode_into_blob(data->hdr, blob, blob_size);
}
-static int metric_scheme_histogram_merge(struct metric_measure_data *pthis, const struct metric_measure_data *from)
+static int metric_scheme_histogram_merge(struct metric_data *pthis, const struct metric_data *from)
{
(void)hdr_add(pthis->hdr, from->hdr);
return 0;
}
-static int metric_scheme_histogram_copy(struct metric_measure_data *pthis, const struct metric_measure_data *from)
+static struct metric_data *metric_scheme_histogram_copy(const struct metric_data *from)
{
- (void)hdr_add(pthis->hdr, from->hdr);
- return 0;
+ struct hdr_histogram* tmp_p_hdr = NULL;
+ hdr_init(from->hdr->lowest_discernible_value, from->hdr->highest_trackable_value, from->hdr->significant_figures, &tmp_p_hdr);
+
+ struct metric_data *data = (struct metric_data *)malloc(sizeof(struct metric_data));
+ data->hdr = tmp_p_hdr;
+
+ hdr_add(data->hdr, from->hdr);
+ return data;
}
-struct metric_measure_data *metric_scheme_histogram_deserialize(const char *blob, size_t blob_size)
+struct metric_data *metric_scheme_histogram_deserialize(const char *blob, size_t blob_size)
{
struct hdr_histogram *hdr = histogram_decode_from_blob(blob, blob_size);
- struct metric_measure_data *data = (struct metric_measure_data *)malloc(sizeof(struct metric_measure_data));
+ struct metric_data *data = (struct metric_data *)malloc(sizeof(struct metric_data));
data->hdr = hdr;
return data;
}
+void metric_scheme_histogram_reset(struct metric_data *data)
+{
+ hdr_reset(data->hdr);
+}
+
static const struct metric_scheme g_metric_scheme_table[] = {
[METRIC_TYPE_COUNTER] = {
.new = metric_scheme_counter_new,
- .del = metric_scheme_counter_free,
+ .free = metric_scheme_counter_free,
.serialize = metric_scheme_counter_serialize,
- .deserialize = metric_scheme_counter_deserialize,
+ // .deserialize = metric_scheme_counter_deserialize,
.merge = metric_scheme_counter_merge,
.copy = metric_scheme_counter_copy,
+ .reset = metric_scheme_counter_reset,
},
[METRIC_TYPE_HLL] = {
.new = metric_scheme_hll_new,
- .del = metric_scheme_hll_free,
+ .free = metric_scheme_hll_free,
.serialize = metric_scheme_hll_serialize,
- .deserialize = metric_scheme_hll_deserialize,
+ // .deserialize = metric_scheme_hll_deserialize,
.merge = metric_scheme_hll_merge,
.copy = metric_scheme_hll_copy,
+ .reset = metric_scheme_hll_reset,
},
[METRIC_TYPE_HISTOGRAM] = {
.new = metric_scheme_histogram_new,
- .del = metric_scheme_histogram_free,
+ .free = metric_scheme_histogram_free,
.serialize = metric_scheme_histogram_serialize,
- .deserialize = metric_scheme_histogram_deserialize,
+ // .deserialize = metric_scheme_histogram_deserialize,
.merge = metric_scheme_histogram_merge,
.copy = metric_scheme_histogram_copy,
+ .reset = metric_scheme_histogram_reset,
},
};
+
/* -------------------------------------------------------------------------- */
/* metric */
/* -------------------------------------------------------------------------- */
-struct metric_parameter *construct_parameters(enum metric_type type, ...)
-{
- struct metric_parameter *paras = (struct metric_parameter *)malloc(sizeof(struct metric_parameter));
- va_list ap;
- va_start(ap, type);
- switch (type) {
- case METRIC_TYPE_COUNTER:
- break;
- case METRIC_TYPE_HLL:
- paras->hll.precision = (char)va_arg(ap, int);
- break;
- case METRIC_TYPE_HISTOGRAM:
- paras->hdr.lowest_trackable_value = va_arg(ap, long long);
- paras->hdr.highest_trackable_value = va_arg(ap, long long);
- paras->hdr.significant_figures = va_arg(ap, int);
- break;
- default:
- assert(0);
- }
- va_end(ap);
- return paras;
-}
-
-struct metric_info *metric_info_new(const char *name, enum metric_type type, struct metric_parameter *para)
-{
- struct metric_info *info = (struct metric_info *)malloc(sizeof(struct metric_info));
- info->name = strdup(name);
- info->paras = para;
- info->type = type;
- info->id = -1;
- return info;
-}
-
-void metric_info_free(struct metric_info *info)
-{
- free(info->name);
- free(info->paras);
- free(info);
-}
-struct metric *metric_new(const char *name, enum metric_type type, struct metric_parameter *para)
+struct metric *metric_new(const struct metric_manifest *manifest)
{
struct metric *pthis = (struct metric *)calloc(1, sizeof(struct metric));
- pthis->info = metric_info_new(name, type, para);
- pthis->scheme = &g_metric_scheme_table[type];
- pthis->max_n_array_item = MAX_N_DATA_ARRAY_ITEM_INIT;
- pthis->data_array = (struct metric_measure_data **)calloc(MAX_N_DATA_ARRAY_ITEM_INIT, sizeof(struct metric_measure_data *));
+ pthis->data = g_metric_scheme_table[manifest->type].new(manifest->parameters);
+ pthis->type = manifest->type;
return pthis;
}
@@ -348,498 +272,106 @@ void metric_free(struct metric *pthis)
if (pthis == NULL) {
return;
}
- metric_info_free(pthis->info);
- for (size_t i = 0; i < pthis->n_array_item; i++) {
- if (pthis->data_array[i] == NULL) {
- continue;
- }
- pthis->scheme->del(pthis->data_array[i]);
- }
- free(pthis->data_array);
+ g_metric_scheme_table[pthis->type].free(pthis->data);
free(pthis);
}
void metric_reset(struct metric *pthis) {
- for (int i = 0; i < pthis->n_array_item; i++) {
- if (pthis->data_array[i] == NULL) {
- continue;
- }
- pthis->scheme->del(pthis->data_array[i]);
- }
- pthis->n_array_item = 0;
- memset(pthis->data_array, 0, sizeof(struct metric_measure_data *) * pthis->max_n_array_item);
-}
-
-void metric_info_copy(struct metric_info *dst, const struct metric_info *src)
-{
- dst->name = strdup(src->name);
- if (src->paras == NULL) {
- dst->paras = NULL;
- } else {
- dst->paras = (struct metric_parameter *)malloc(sizeof(struct metric_parameter));
- memcpy(dst->paras, src->paras, sizeof(struct metric_parameter));
- }
-
- dst->type = src->type;
- dst->id = src->id;
-}
-
-struct metric *metric_fork(const struct metric *src)
-{
- struct metric *dst = (struct metric *)malloc(sizeof(struct metric));
- dst->info = (struct metric_info *)malloc(sizeof(struct metric_info));
- metric_info_copy(dst->info, src->info);
- dst->scheme = &g_metric_scheme_table[src->info->type];
-
- dst->n_array_item = 0;
- dst->max_n_array_item = src->max_n_array_item;
- dst->data_array = (struct metric_measure_data **)calloc(src->max_n_array_item, sizeof(struct metric_measure_data *));
-
- return dst;
-}
-
-struct metric *metric_copy(const struct metric *src)
-{
- struct metric *dst = metric_fork(src);
- dst->n_array_item = src->n_array_item;
-
- for (int i = 0; i < src->n_array_item; i++) {
- if (src->data_array[i] == NULL) {
- continue;
- }
- struct metric_measure_data *dest_data = src->scheme->new(src->info->paras);
- src->scheme->copy(dest_data, src->data_array[i]);
- dst->data_array[i] = dest_data;
- }
- return dst;
-}
-
-/*
-
----------------------------------- counter ----------------------------------
-{
- "type": 0
- "merge_type"
-}
-
----------------------------------- hll ----------------------------------
-{
- "type": 1,
- "prec": <precision value>
-}
-
----------------------------------- histogram ----------------------------------
-{
- "type": 2,
- "min": <lowest_trackable_value>
- "max": <highest_trackable_value>
- "prec": <significant_figures>
-
-type:uint32 | min: long long | max: long long | prec: uint32
-}
-*/
-void metric_serialize_parameters(const struct metric *metric, char **blob, size_t *blob_size)
-{
- struct metric_parameter *para = metric->info->paras;
- struct fs_reader *reader = fs_reader_new();
-
- fs_reader_read_uint(reader, metric->info->type);
-
- switch (metric->info->type)
- {
- case METRIC_TYPE_COUNTER:
- break;
- case METRIC_TYPE_HLL:
- fs_reader_read_uint(reader, para->hll.precision);
- break;
- case METRIC_TYPE_HISTOGRAM:
- fs_reader_read_longlong(reader, para->hdr.lowest_trackable_value);
- fs_reader_read_longlong(reader, para->hdr.highest_trackable_value);
- fs_reader_read_uint(reader, para->hdr.significant_figures);
- break;
- default:
- break;
- }
-
- fs_reader_finalize(reader, blob, blob_size);
-}
-
-struct metric_parameter *deserialize_parameters(const char *blob, size_t blob_size)
-{
- struct fs_writer *writer = fs_writer_new(blob, blob_size);
-
- enum metric_type type;
- fs_writer_write_uint(writer, (uint32_t *)&type);
-
- struct metric_parameter *field_para = NULL;
- switch (type) {
- case METRIC_TYPE_COUNTER:
-
- break;
- case METRIC_TYPE_HLL: {
- int precision;
- fs_writer_write_uint(writer, (uint32_t *)&precision);
- field_para = construct_parameters(type, precision);
- }
- break;
- case METRIC_TYPE_HISTOGRAM: {
- long long min;
- long long max;
- int prec;
- fs_writer_write_longlong(writer, &min);
- fs_writer_write_longlong(writer, &max);
- fs_writer_write_uint(writer, (uint32_t *)&prec);
-
- field_para = construct_parameters(type, min, max, prec);
- }
- break;
- default:
- break;
- }
-
- fs_writer_free(writer);
- return field_para;
-}
-
-/*
-{
- "id":<cell_id>,
- "PL":<content_blob>
-
-id:uint | PL:bin
-}
-*/
-void blob_wrap_basic_info(int cell_id, const char *blob, size_t blob_size, char **new_blob, size_t *new_blob_size)
-{
- struct fs_reader *reader = fs_reader_new();
-
- fs_reader_read_uint(reader, cell_id);
- fs_reader_read_bin(reader, blob, blob_size);
-
- fs_reader_finalize(reader, new_blob, new_blob_size);
-}
-
-// user must free new_blob
-void blob_unpack_basic_info(const char *blob, size_t blob_size, char **new_blob, size_t *new_blob_size, int *cell_id)
-{
- struct fs_writer *writer = fs_writer_new(blob, blob_size);
-
- fs_writer_write_uint(writer, (uint32_t *)cell_id);
- fs_writer_write_bin(writer, new_blob, new_blob_size);
-
- fs_writer_free(writer);
-}
-
-/*
-metric blob:
- {
- "name":<metric_name>
- "type":<field_type>
- "id":<field_id>
- "para":[may be null, one number, or a double array]
- "data":[{
- "id":<cell_id>
- "PL":<content_blob(gen by scheme)>
- }]
-}
-
-name:string | type:uint | para:bin | data_id:int_array | data_payload:bin_array
-*/
-int metric_serialize(const struct metric *pthis, char **blob, size_t *blob_size)
-{
- struct fs_reader *reader = fs_reader_new();
-
- fs_reader_read_str(reader, pthis->info->name);
- fs_reader_read_uint(reader, pthis->info->type);
-
- char *para_blob = NULL;
- size_t para_blob_size = 0;
- metric_serialize_parameters(pthis, &para_blob, &para_blob_size);
- fs_reader_read_bin(reader, para_blob, para_blob_size);
- free(para_blob);
-
- int *cell_ids = NULL;
- size_t cell_ids_size = 0;
- metric_get_cell_ids(pthis, &cell_ids, &cell_ids_size);
- if (cell_ids == NULL) {
- fs_reader_read_nil(reader);
- } else {
- fs_reader_start_bin_array(reader, cell_ids_size);
- for (int i = 0; i < cell_ids_size; i++) {
- struct metric_measure_data *data = metric_find_one_cell(pthis, cell_ids[i]);
-
- char *data_blob = NULL;
- size_t data_blob_size = 0;
- pthis->scheme->serialize(data, &data_blob, &data_blob_size);
-
- char *tmp_data_wrapped_blob = NULL;
- size_t tmp_data_wrapped_blob_size = 0;
- blob_wrap_basic_info(cell_ids[i], data_blob, data_blob_size, &tmp_data_wrapped_blob, &tmp_data_wrapped_blob_size);
- fs_reader_read_bin(reader, tmp_data_wrapped_blob, tmp_data_wrapped_blob_size);
- free(data_blob);
- free(tmp_data_wrapped_blob);
- }
-
- free(cell_ids);
- }
-
- fs_reader_finalize(reader, blob, blob_size);
-
- return 0;
+ g_metric_scheme_table[pthis->type].reset(pthis->data);
+ pthis->operated_after_reset = false;
}
-struct metric *metric_deserialize(const char *blob, size_t blob_size)
-{
- struct fs_writer *writer = fs_writer_new(blob, blob_size);
+struct metric *metric_copy(const struct metric *src) {
+ struct metric *dest = (struct metric *)malloc(sizeof(struct metric));
+ dest->type = src->type;
+ dest->data = g_metric_scheme_table[dest->type].copy(src->data);
+ dest->operated_after_reset = src->operated_after_reset;
- char *metric_name;
- fs_writer_write_str(writer, &metric_name);
- enum metric_type type;
- fs_writer_write_uint(writer, (uint32_t *)&type);
-
- char *para_blob;
- size_t para_blob_size;
- fs_writer_write_bin(writer, &para_blob, &para_blob_size);
- struct metric_parameter *metric_para = deserialize_parameters(para_blob, para_blob_size);
- struct metric *metric_out = metric_new(metric_name, type, metric_para);
- free(metric_name);
- free(para_blob);
-
- int ret = fs_writer_expect_nil(writer);
- assert(ret >= 0);
- if (ret == 1) { // no cells in this metric
- fs_writer_free(writer);
- return metric_out;
- }
-
- char **data_blob_raw = NULL;
- size_t *data_blob_raw_size = NULL;
- size_t n_data_blob_raw = 0;
- fs_writer_write_bin_array(writer, &data_blob_raw, &n_data_blob_raw, &data_blob_raw_size);
-
- for (int i = 0; i < n_data_blob_raw; i++) {
- int cell_id;
- char *data_blob = NULL;
- size_t data_blob_size = 0;
- blob_unpack_basic_info(data_blob_raw[i], data_blob_raw_size[i], &data_blob, &data_blob_size, &cell_id);
- struct metric_measure_data *data = metric_out->scheme->deserialize(data_blob, data_blob_size);
- free(data_blob);
- metric_add_one_cell(metric_out, cell_id, data);
- }
-
- for (int i = 0; i < n_data_blob_raw; i++) {
- free(data_blob_raw[i]);
- }
- free(data_blob_raw);
- free(data_blob_raw_size);
- fs_writer_free(writer);
-
- return metric_out;
+ return dest;
}
-int metric_merge_or_copy_cell(struct metric *dest, const struct metric *src, int dest_cell_id, int src_cell_id)
-{
- struct metric_measure_data *src_data = metric_find_one_cell(src, src_cell_id);
- if (src_data == NULL) {
- return 1;
- }
-
- struct metric_measure_data *dest_data = metric_find_one_cell(dest, dest_cell_id);
- if (dest_data == NULL) { // copy
- dest_data = src->scheme->new(src->info->paras);
- int ret = dest->scheme->copy(dest_data, src_data);
- if (ret == -1) {
- dest->scheme->del(dest_data);
- return -1;
- }
- metric_add_one_cell(dest, dest_cell_id, dest_data);
- return 0;
+//return -1 when merge error. 0 when success. 1 when src has no cell.
+int metric_merge(struct metric *dest, const struct metric *src) {
+ if (src->operated_after_reset) {
+ dest->operated_after_reset = true;
}
-
- //merge
- return dest->scheme->merge(dest_data, src_data);
+ return g_metric_scheme_table[dest->type].merge(dest->data, src->data);
}
-void metric_get_plain_blob(const struct metric *pthis, int cell_id, char **blob, size_t *blob_size)
-{
- struct metric_measure_data *data = metric_find_one_cell(pthis, cell_id);
- if (data == NULL) {
- *blob = NULL;
- *blob_size = 0;
+void metric_serialize(const struct metric *pthis, char **blob, size_t *blob_size) {
+ struct metric_data *data = pthis->data;
+ enum metric_type type = pthis->type;
+ if (type == METRIC_TYPE_HLL) {
+ hyperloglog_serialize_into_base64(data->hll, blob, blob_size);
return;
}
- if (pthis->info->type == METRIC_TYPE_HLL) {
- ST_hyperloglog_serialize_for_networking(data->hll, blob, blob_size);
- return;
- }
- if (pthis->info->type == METRIC_TYPE_HISTOGRAM) {
+ if (type == METRIC_TYPE_HISTOGRAM) {
histogram_encode_into_b64(data->hdr, blob, blob_size);
return;
}
- pthis->scheme->serialize(data, blob, blob_size);
+ g_metric_scheme_table[type].serialize(data, blob, blob_size);
}
-void metric_delete_cell(struct metric *pthis, int cell_id)
-{
- struct metric_measure_data *data = metric_find_one_cell(pthis, cell_id);
- if (data == NULL) {
- return;
- }
- pthis->scheme->del(data);
- pthis->data_array[cell_id] = NULL;
+bool metric_check_if_cleared(const struct metric *pthis) {
+ return !pthis->operated_after_reset;
}
/* -------------------------------------------------------------------------- */
-/* metric operations */
+/* specific metric operations */
/* -------------------------------------------------------------------------- */
-static inline struct metric_measure_data *metric_find_or_new_cell(struct metric *pthis, int cell_id)
-{
- struct metric_measure_data *data = metric_find_one_cell(pthis, cell_id);
-
- if (data == NULL) {
- data = pthis->scheme->new(pthis->info->paras);
- metric_add_one_cell(pthis, cell_id, data);
- }
- return data;
-}
+void metric_counter_incrby(struct metric *pthis, long long value) {
+ struct metric_counter_or_gauge *counter = pthis->data->counter;
+ counter->value += value;
-struct metric *metric_counter_new(const char *name)
-{
- struct metric_parameter *metric_para = NULL;
- struct metric *pthis = metric_new(name, METRIC_TYPE_COUNTER, metric_para);
-
- return pthis;
+ pthis->operated_after_reset = true;
}
-void metric_counter_incrby(struct metric *pthis, int cell_id, long long value)
-{
- struct metric_measure_data *data = metric_find_or_new_cell(pthis, cell_id);
- data->counter->value += value;
-}
-
-int metric_counter_set(struct metric *pthis, int cell_id, long long value)
-{
- struct metric_measure_data *data = metric_find_or_new_cell(pthis, cell_id);
- data->counter->value = value;
+void metric_counter_set(struct metric *pthis, long long value) {
+ struct metric_counter_or_gauge *counter = pthis->data->counter;
+ counter->value = value;
- return 0;
+ pthis->operated_after_reset = true;
}
-int metric_counter_get(const struct metric *pthis, int cell_id, long long *value_out)
-{
- const struct metric_measure_data *data = metric_find_one_cell(pthis, cell_id);
- if (data == NULL) {
- *value_out = 0;
- return -1;
- }
- const struct metric_counter_or_gauge *counter = data->counter;
- *value_out = counter->value;
- return 0;
+long long metric_counter_get(const struct metric *pthis) {
+ return pthis->data->counter->value;
}
-struct metric *metric_hll_new(const char *name, unsigned char precision)
-{
- struct metric_parameter *metric_para = construct_parameters(METRIC_TYPE_HLL, precision);
- struct metric *pthis = metric_new(name, METRIC_TYPE_HLL, metric_para);
+void metric_hll_add(struct metric *pthis, const char *key, size_t key_len) {
+ hyperloglog_add(pthis->data->hll, key, key_len);
- return pthis;
+ pthis->operated_after_reset = true;
}
-void metric_hll_add(struct metric *pthis, int cell_id, const char *key, size_t key_len)
-{
- struct metric_measure_data *data = metric_find_or_new_cell(pthis, cell_id);
+void metric_hll_add_hash(struct metric *pthis, unsigned long long hash) {
+ hyperloglog_add_hash(pthis->data->hll, hash);
- ST_hyperloglog_add(data->hll, key, key_len);
+ pthis->operated_after_reset = true;
}
-double metric_hll_get(const struct metric *pthis, int cell_id)
-{
- const struct metric_measure_data *data = metric_find_one_cell(pthis, cell_id);
- if (data == NULL) {
- return -1;
- }
- return ST_hyperloglog_count(data->hll);
+double metric_hll_get(const struct metric *pthis) {
+ return hyperloglog_count(pthis->data->hll);
}
-struct metric *metric_histogram_new(const char *name, long long lowest_trackable_value, long long highest_trackable_value, int significant_figures)
-{
- struct metric_parameter *metric_para = construct_parameters(METRIC_TYPE_HISTOGRAM, lowest_trackable_value, highest_trackable_value, significant_figures);
- struct metric *pthis = metric_new(name, METRIC_TYPE_HISTOGRAM, metric_para);
-
- return pthis;
-}
-
-int metric_histogram_record(struct metric *pthis, int cell_id, long long value)
-{
- struct metric_measure_data *data = metric_find_or_new_cell(pthis, cell_id);
- if (value > data->hdr->highest_trackable_value) {
- value = data->hdr->highest_trackable_value;
+int metric_histogram_record(struct metric *pthis, long long value) {
+ if (value > pthis->data->hdr->highest_trackable_value) {
+ value = pthis->data->hdr->highest_trackable_value;
}
-
- bool ret = hdr_record_value(data->hdr, value);
+ bool ret = hdr_record_value(pthis->data->hdr, value);
if (!ret) {
return -1;
}
+
+ pthis->operated_after_reset = true;
return 0;
}
-long long metric_histogram_value_at_percentile(const struct metric *pthis, int cell_id, double percentile)
-{
- const struct metric_measure_data *data = metric_find_one_cell(pthis, cell_id);
- if (data == NULL) {
- return -1;
- }
- return hdr_value_at_percentile(data->hdr, percentile);
+long long metric_histogram_value_at_percentile(const struct metric *pthis, double percentile) {
+ return hdr_value_at_percentile(pthis->data->hdr, percentile);
}
-long long metric_histogram_count_le_value(const struct metric *pthis, int cell_id, long long value)
-{
- const struct metric_measure_data *data = metric_find_one_cell(pthis, cell_id);
- if (data == NULL) {
- return -1;
- }
- return hdr_count_le_value(data->hdr, value);
+long long metric_histogram_count_le_value(const struct metric *pthis, long long value) {
+ return hdr_count_le_value(pthis->data->hdr, value);
}
-
-/* -------------------------------------------------------------------------- */
-/* query on the metric */
-/* -------------------------------------------------------------------------- */
-void metric_get_cell_ids(const struct metric *pthis, int **cell_ids, size_t *cell_id_count)
-{
- size_t n_id_max = pthis->n_array_item;
- if (n_id_max == 0) {
- *cell_ids = NULL;
- *cell_id_count = 0;
- return;
- }
-
- int *tmp_id_container = (int *)malloc(sizeof(int) * n_id_max);
- int n_id = 0;
- for (int i = 0; i < n_id_max; i++) {
- if (pthis->data_array[i] != NULL) {
- tmp_id_container[n_id++] = i;
- }
- }
-
- if (n_id == 0) {
- *cell_ids = NULL;
- *cell_id_count = 0;
- free(tmp_id_container);
- return;
- }
-
- *cell_id_count = n_id;
- *cell_ids = tmp_id_container;
-}
-
-const char *metric_get_name(const struct metric *metrics)
-{
- return metrics->info->name;
-}
-
-enum metric_type metric_get_type(const struct metric *metrics)
-{
- return metrics->info->type;
-} \ No newline at end of file
diff --git a/src/metrics/metric.h b/src/metrics/metric.h
index 845a792..0dee126 100644
--- a/src/metrics/metric.h
+++ b/src/metrics/metric.h
@@ -2,38 +2,29 @@
#include <stddef.h>
#include <stdbool.h>
-#include "fieldstat.h"
+#include "fieldstat.h" // for enum metric_type
+#include "metric_manifest.h"
struct metric;
-const char *metric_get_name(const struct metric *metrics); // don't free the return value
-enum metric_type metric_get_type(const struct metric *metrics);
void metric_free(struct metric *pthis);
void metric_reset(struct metric *pthis);
struct metric *metric_copy(const struct metric *src);
-struct metric *metric_fork(const struct metric *src);
-int metric_serialize(const struct metric *pthis, char **blob, size_t *blob_size);
-struct metric *metric_deserialize(const char *blob, size_t blob_size);
+struct metric *metric_new(const struct metric_manifest *manifest);
+int metric_merge(struct metric *dest, const struct metric *src);
+void metric_serialize(const struct metric *pthis, char **blob, size_t *blob_size);
+bool metric_check_if_cleared(const struct metric *pthis);
-//return -1 when merge error. 0 when success. 1 when src has no cell.
-int metric_merge_or_copy_cell(struct metric *dest, const struct metric *src, int dest_cell_id, int src_cell_id);
-void metric_get_plain_blob(const struct metric *pthis, int cell_id, char **blob, size_t *blob_size);
-void metric_delete_cell(struct metric *pthis, int cell_id);
+void metric_counter_incrby(struct metric *pthis, long long value);
+void metric_counter_set(struct metric *pthis, long long value);
+long long metric_counter_get(const struct metric *pthis);
-struct metric *metric_counter_new(const char *name);
-void metric_counter_incrby(struct metric *pthis, int cell_id, long long value);
-int metric_counter_set(struct metric *pthis, int cell_id, long long value);
-int metric_counter_get(const struct metric *pthis, int cell_id, long long *value_out);
+void metric_hll_add(struct metric *pthis, const char *key, size_t key_len);
+void metric_hll_add_hash(struct metric *pthis, unsigned long long hash);
+double metric_hll_get(const struct metric *pthis);
-struct metric *metric_hll_new(const char *name, unsigned char precision);
-void metric_hll_add(struct metric *pthis, int cell_id, const char *key, size_t key_len);
-double metric_hll_get(const struct metric *pthis, int cell_id);
-
-struct metric *metric_histogram_new(const char *name, long long lowest_trackable_value, long long highest_trackable_value, int significant_figures);
-int metric_histogram_record(struct metric *pthis, int cell_id, long long value);
+int metric_histogram_record(struct metric *pthis, long long value);
// for the meaning of these two query method, refer to https://prometheus.io/docs/concepts/metric_types/, and search for "histogram" and "summary"
-long long metric_histogram_value_at_percentile(const struct metric *pthis, int cell_id, double percentile);
-long long metric_histogram_count_le_value(const struct metric *pthis, int cell_id, long long value);
-
+long long metric_histogram_value_at_percentile(const struct metric *pthis, double percentile);
+long long metric_histogram_count_le_value(const struct metric *pthis, long long value);
-void metric_get_cell_ids(const struct metric *pthis, int **cell_ids, size_t *cell_id_count);
diff --git a/src/metrics/metric_manifest.c b/src/metrics/metric_manifest.c
new file mode 100644
index 0000000..fd03209
--- /dev/null
+++ b/src/metrics/metric_manifest.c
@@ -0,0 +1,134 @@
+
+#include "metric_manifest.h"
+
+#define DEFAULT_N_METRIC 32
+
+struct metric_manifest; // defined in metric_manifest.h
+
+struct metric_manifest_manager {
+ struct metric_manifest *hash_table;
+
+ struct metric_manifest **manifests;
+ size_t manifest_size;
+ size_t manifest_cnt;
+};
+
+struct metric_manifest_manager *metric_manifest_manager_new() {
+ struct metric_manifest_manager *ret = calloc(1, sizeof(struct metric_manifest_manager));
+
+ ret->manifest_size = DEFAULT_N_METRIC;
+ ret->manifests = calloc(DEFAULT_N_METRIC, sizeof(struct metric_manifest *));
+ ret->manifest_cnt = 0;
+
+ return ret;
+}
+
+void metric_manifest_manager_free(struct metric_manifest_manager *pthis) {
+ struct metric_manifest *cur, *tmp;
+ HASH_ITER(hh, pthis->hash_table, cur, tmp) {
+ HASH_DEL(pthis->hash_table, cur);
+ }
+
+ for (size_t i = 0; i < pthis->manifest_cnt; i++) {
+ metric_manifest_free(pthis->manifests[i]);
+ }
+ free(pthis->manifests);
+ free(pthis);
+}
+
+const struct metric_manifest* metric_manifest_manager_get_by_id(const struct metric_manifest_manager *pthis, int metric_id) {
+ if (metric_id >= pthis->manifest_cnt || metric_id < 0) {
+ return NULL;
+ }
+ return pthis->manifests[metric_id];
+}
+
+const struct metric_manifest* metric_manifest_manager_get_by_name(const struct metric_manifest_manager *pthis, const char *name) {
+ struct metric_manifest *manifest = NULL;
+ HASH_FIND_STR(pthis->hash_table, name, manifest);
+ return manifest;
+}
+
+void add_manifest_to_position(struct metric_manifest_manager *pthis, struct metric_manifest *manifest, int metric_id)
+{
+ if (metric_id >= pthis->manifest_size) {
+ pthis->manifests = realloc(pthis->manifests, sizeof(struct metric_manifest *) * pthis->manifest_size * 2);
+ memset(pthis->manifests + pthis->manifest_size, 0, sizeof(struct metric_manifest *) * (pthis->manifest_size));
+ pthis->manifest_size *= 2;
+ }
+
+ pthis->manifests[metric_id] = manifest;
+ if (metric_id >= pthis->manifest_cnt) {
+ pthis->manifest_cnt = metric_id + 1;
+ }
+
+ HASH_ADD_KEYPTR(hh, pthis->hash_table, manifest->name, strlen(manifest->name), manifest);
+}
+
+int metric_manifest_manager_add(struct metric_manifest_manager *pthis, struct metric_manifest *manifest) {
+ struct metric_manifest *tmp;
+ HASH_FIND_STR(pthis->hash_table, manifest->name, tmp);
+ if (tmp != NULL) {
+ return -1;
+ }
+
+ int id = pthis->manifest_cnt;
+ add_manifest_to_position(pthis, manifest, id);
+
+ manifest->id = id;
+ return id;
+}
+
+const struct metric_manifest **metric_manifest_manager_list(const struct metric_manifest_manager *pthis, size_t *n_manifest) {
+ *n_manifest = pthis->manifest_cnt;
+ return (const struct metric_manifest **)pthis->manifests;
+}
+
+bool metric_manifest_manager_equal(const struct metric_manifest_manager *pthis, const struct metric_manifest_manager *other) {
+ if (pthis->manifest_cnt != other->manifest_cnt) {
+ return false;
+ }
+
+ for (size_t i = 0; i < pthis->manifest_cnt; i++) {
+ if (pthis->manifests[i]->type != other->manifests[i]->type) {
+ return false;
+ }
+ if (strcmp(pthis->manifests[i]->name, other->manifests[i]->name) != 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void metric_manifest_free(struct metric_manifest *manifest) {
+ free(manifest->name);
+ free(manifest->parameters);
+ free(manifest);
+}
+
+struct metric_manifest *metric_manifest_copy(const struct metric_manifest *src)
+{
+ struct metric_manifest *dest = (struct metric_manifest *)malloc(sizeof(struct metric_manifest));
+ dest->name = strdup(src->name);
+ if (src->parameters == NULL) {
+ dest->parameters = NULL;
+ } else {
+ dest->parameters = (union metric_parameter *)malloc(sizeof(union metric_parameter));
+ memcpy(dest->parameters, src->parameters, sizeof(union metric_parameter));
+ }
+
+ dest->type = src->type;
+ dest->id = src->id;
+
+ return dest;
+}
+
+struct metric_manifest_manager *metric_manifest_manager_copy(const struct metric_manifest_manager *pthis) {
+ struct metric_manifest_manager *ret = metric_manifest_manager_new();
+ for (size_t i = 0; i < pthis->manifest_cnt; i++) {
+ struct metric_manifest *manifest = metric_manifest_copy(pthis->manifests[i]);
+ metric_manifest_manager_add(ret, manifest);
+ }
+ return ret;
+}
diff --git a/src/metrics/metric_manifest.h b/src/metrics/metric_manifest.h
new file mode 100644
index 0000000..632b314
--- /dev/null
+++ b/src/metrics/metric_manifest.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "uthash.h"
+#include "fieldstat.h"
+
+struct histogram_parameters {
+ long long lowest_trackable_value;
+ long long highest_trackable_value;
+ int significant_figures;
+};
+
+struct hll_parameters {
+ char precision;
+};
+
+union metric_parameter {
+ struct histogram_parameters hdr;
+ struct hll_parameters hll;
+};
+
+struct metric_manifest {
+ int id;
+ char *name;
+ union metric_parameter *parameters;
+ enum metric_type type;
+
+ UT_hash_handle hh;
+};
+
+struct metric_manifest *metric_manifest_copy(const struct metric_manifest *src);
+void metric_manifest_free(struct metric_manifest *pthis);
+
+struct metric_manifest_manager;
+
+struct metric_manifest_manager *metric_manifest_manager_new();
+void metric_manifest_manager_free(struct metric_manifest_manager *pthis);
+int metric_manifest_manager_add(struct metric_manifest_manager *pthis, struct metric_manifest *manifest);
+struct metric_manifest_manager *metric_manifest_manager_copy(const struct metric_manifest_manager *pthis);
+
+const struct metric_manifest **metric_manifest_manager_list(const struct metric_manifest_manager *pthis, size_t *n_manifest);
+const struct metric_manifest*metric_manifest_manager_get_by_id(const struct metric_manifest_manager *pthis, int metric_id);
+const struct metric_manifest*metric_manifest_manager_get_by_name(const struct metric_manifest_manager *pthis, const char *name); \ No newline at end of file
diff --git a/src/metrics/python_api.c b/src/metrics/python_api.c
index c7c4c6d..3f52149 100644
--- a/src/metrics/python_api.c
+++ b/src/metrics/python_api.c
@@ -5,7 +5,7 @@
#include "histogram_encoder.h"
#include "base64/b64.h"
-#include "st_hyperloglog.h"
+#include "hyperloglog.h"
// user must free the buf after use
void *fieldstat_histogram_base64_decode(char *buf)
@@ -16,12 +16,12 @@ void *fieldstat_histogram_base64_decode(char *buf)
return hdr;
}
-long long fieldstat_histogram_value_at_percentile(void* h, double percentile)
+long long fieldstat_histogram_value_at_percentile_api(void* h, double percentile)
{
return hdr_value_at_percentile((const struct hdr_histogram *)h, percentile);
}
-long long fieldstat_histogram_count_le_value(void* h, long long value)
+long long fieldstat_histogram_count_le_value_api(void* h, long long value)
{
return hdr_count_le_value((const struct hdr_histogram *)h, value);
}
@@ -75,9 +75,9 @@ void fieldstat_histogram_free(void *h)
void *hll_base64_decode(char *buf);
double fieldstat_hll_base64_to_count(char *buf)
{
- struct ST_hyperloglog *hll = hll_base64_decode(buf);
- double count = ST_hyperloglog_count(hll);
- ST_hyperloglog_free(hll);
+ struct hyperloglog *hll = hll_base64_decode(buf);
+ double count = hyperloglog_count(hll);
+ hyperloglog_free(hll);
return count;
}
@@ -101,7 +101,7 @@ void *hll_base64_decode(char *buf)
unsigned char precision;
memcpy(&precision, dec + sizeof(unsigned char), sizeof(unsigned char));
- struct ST_hyperloglog *hll_from_blob = ST_hyperloglog_new(precision);
+ struct hyperloglog *hll_from_blob = hyperloglog_new(precision);
int num_reg = NUM_REG(precision);
int words = INT_CEIL(num_reg, REG_PER_WORD);
@@ -120,7 +120,7 @@ void *hll_base64_decode(char *buf)
void fieldstat_hll_free(void *hll)
{
- ST_hyperloglog_free((struct ST_hyperloglog *)hll);
+ hyperloglog_free((struct hyperloglog *)hll);
}
// cppcheck-suppress [constParameterPointer, unmatchedSuppression]
diff --git a/src/tags/cell_manager.c b/src/tags/cell_manager.c
deleted file mode 100644
index 67e0cbf..0000000
--- a/src/tags/cell_manager.c
+++ /dev/null
@@ -1,399 +0,0 @@
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "fieldstat.h"
-#include "tags/cell_manager.h"
-#include "tags/heavy_keeper.h"
-#include "tags/my_ut_hash_inner.h"
-#include "tags/my_ut_hash.h"
-#include "serializer.h"
-
-
-struct tag_id_map {
- struct tag_hash_key *tag;
- int cell_id;
- bool dying;
- UT_hash_handle hh;
-};
-
-struct cell_manager {
- union {
- struct heavy_keeper *topk_tag_id_map;
- struct tag_id_map *comprehensive_tag_id_map;
- };
- struct tag_hash_key **id_tag_array;
- int id_tag_array_len;
- int max_cell_id;
-
- enum sampling_mode sampling_mode;
- int max_cell_num;
- int current_cell_num;
- int next_cell_id;
-};
-
-struct cube_manager {
- struct tag_id_map *head;
-};
-
-struct cell_manager *cell_manager_new_without_map(enum sampling_mode sampling_mode, int max_cell_num)
-{
- struct cell_manager *cell_manager = (struct cell_manager *)calloc(1, sizeof(struct cell_manager));
-
- cell_manager->sampling_mode = sampling_mode;
- cell_manager->max_cell_num = max_cell_num;
- cell_manager->next_cell_id = 0;
-
- cell_manager->id_tag_array_len = max_cell_num;
- if (cell_manager->id_tag_array_len > 4096) { // support dynamic size cube (especially when max_max_num is unlimited)
- cell_manager->id_tag_array_len = 4096;
- }
- cell_manager->id_tag_array = calloc(cell_manager->id_tag_array_len, sizeof(struct tag_hash_key *));
- cell_manager->max_cell_id = -1;
-
- return cell_manager;
-}
-
-struct cell_manager *cell_manager_new(enum sampling_mode sampling_mode, int max_cell_num)
-{
- struct cell_manager *cell_manager = cell_manager_new_without_map(sampling_mode, max_cell_num);
-
- if (sampling_mode == SAMPLING_MODE_TOPK) {
- cell_manager->topk_tag_id_map = heavy_keeper_with_data_new(max_cell_num);
- } else {
- cell_manager->comprehensive_tag_id_map = NULL;
- }
-
- return cell_manager;
-}
-
-void tag_id_map_free(struct tag_id_map *head)
-{
- struct tag_id_map *node = NULL;
- struct tag_id_map *tmp = NULL;
- HASH_ITER(hh, head, node, tmp) {
- HASH_DEL(head, node);
- tag_hash_key_free(node->tag);
- free(node);
- }
-}
-
-void cell_manager_reset(struct cell_manager *pthis)
-{
- pthis->next_cell_id = 0;
- pthis->max_cell_id = -1;
- pthis->current_cell_num = 0;
- memset(pthis->id_tag_array, 0, sizeof(struct tag_hash_key *) * pthis->id_tag_array_len);
-
- if (pthis->sampling_mode == SAMPLING_MODE_TOPK) {
- heavy_keeper_reset(pthis->topk_tag_id_map);
- return;
- }
-
- struct tag_id_map *node = NULL;
- struct tag_id_map *tmp = NULL;
- HASH_ITER(hh, pthis->comprehensive_tag_id_map, node, tmp) {
- if (!node->dying) {
- node->dying = true;
- continue;
- }
- HASH_DEL(pthis->comprehensive_tag_id_map, node);
- tag_hash_key_free(node->tag);
- free(node);
- }
-}
-
-void cell_manager_free(struct cell_manager *pthis)
-{
- free(pthis->id_tag_array);
-
- if (pthis->sampling_mode == SAMPLING_MODE_TOPK) {
- heavy_keeper_free(pthis->topk_tag_id_map);
- } else {
- tag_id_map_free(pthis->comprehensive_tag_id_map);
- }
-
- free(pthis);
-}
-
-const struct tag_hash_key *cell_manager_get_tag_by_cell_id(const struct cell_manager *pthis, int cell_id)
-{
- return pthis->id_tag_array[cell_id];
-}
-
-void cell_manager_id_tag_array_add(struct cell_manager *pthis, int cell_id, struct tag_hash_key *tag)
-{
- if (cell_id >= pthis->id_tag_array_len) {
- int new_len = pthis->id_tag_array_len;
- while (cell_id >= new_len) {
- new_len *= 2;
- }
-
- struct tag_hash_key **new_array = (struct tag_hash_key **)calloc(new_len, sizeof(struct tag_hash_key *));
- assert(new_array != NULL);
- memcpy(new_array, pthis->id_tag_array, sizeof(struct tag_hash_key *) * pthis->id_tag_array_len);
- free(pthis->id_tag_array);
- pthis->id_tag_array = new_array;
- pthis->id_tag_array_len = new_len;
- }
-
- pthis->id_tag_array[cell_id] = tag;
-
- if (cell_id > pthis->max_cell_id) {
- pthis->max_cell_id = cell_id;
- }
- pthis->current_cell_num++;
-}
-
-long long cell_manager_get_count_by_tag(const struct cell_manager *pthis, const struct tag_hash_key *tag)
-{
- if (pthis->sampling_mode == SAMPLING_MODE_COMPREHENSIVE) {
- return -1;
- }
-
- unsigned long long int count = 0;
- int ret = heavy_keeper_query_one(pthis->topk_tag_id_map, tag, &count, NULL);
- if (ret == -1) {
- return -1;
- }
-
- return count;
-}
-
-int cell_manager_add_cell(struct cell_manager *pthis, const struct tag_hash_key *tag)
-{
- struct tag_id_map *node = NULL;
- HASH_FIND_TAG(pthis->comprehensive_tag_id_map, tag, node);
- if (node != NULL && !node->dying) {
- return node->cell_id;
- }
-
- if (pthis->current_cell_num >= pthis->max_cell_num) {
- return -1;
- }
-
- if (node != NULL) {
- node->dying = false;
- node->cell_id = pthis->next_cell_id;
- cell_manager_id_tag_array_add(pthis, pthis->next_cell_id, node->tag);
- return pthis->next_cell_id++;
- }
-
- struct tag_hash_key *tag_copy = tag_hash_key_copy(tag);
- node = (struct tag_id_map *)malloc(sizeof(struct tag_id_map));
- node->tag = tag_copy;
- node->cell_id = pthis->next_cell_id;
- node->dying = false;
- HASH_ADD_TAG(pthis->comprehensive_tag_id_map, tag, node);
-
- cell_manager_id_tag_array_add(pthis, pthis->next_cell_id, tag_copy);
- return pthis->next_cell_id++;
-}
-
-int cell_manager_add_cell_topk(struct cell_manager *pthis, const struct tag_hash_key *tag, unsigned long long int count, int *popped_cell_id)
-{
- int ret = heavy_keeper_add(pthis->topk_tag_id_map, tag, count, pthis->next_cell_id, popped_cell_id);
- if (ret == -1) { // didn't add
- *popped_cell_id = -1;
- return -1;
- }
-
- if (ret == 1) { // use the original id
- int existing_cell_id = -1;
- heavy_keeper_query_one(pthis->topk_tag_id_map, tag, NULL, &existing_cell_id);
-
- *popped_cell_id = -1;
- return existing_cell_id;
- }
-
- struct tag_hash_key *tag_ret = heavy_keeper_get_true_pst_tag(pthis->topk_tag_id_map, tag);
-
- /* -------------------------------- new cell added-------------------------------- */
-/*
-To mitigate the risk of unbounded array size due to potentially infinite growth of cell IDs, a maximum limit is set for cell IDs in the metric.
-This limit is configured as 8 times the value of K. Even in the worst-case scenario of malicious attacks, this prevents excessive memory usage.
-Even though there is a small possibility of not counting certain flows due to the limit, it is expected that the unallocated IDs would correspond to mouse flows,
- which would have minimal impact on accuracy.
-The number 8 was determined through experiment, where multiple tests were conducted, randomly generating flows by which cells are determined.
- The observed stable cell ID count did not exceed 3 times the value of K. Therefore, a conservative value of 8 was chosen.*/
- if (pthis->next_cell_id >= pthis->max_cell_num * 8) { // max int is much larger, no risk of overflow
- return -1;
- }
-
- if (*popped_cell_id != -1) { // popped, no need to free the tag
- pthis->id_tag_array[*popped_cell_id] = NULL;
- }
- cell_manager_id_tag_array_add(pthis, pthis->next_cell_id, tag_ret);
- return pthis->next_cell_id++;
-}
-
-int cell_manager_delete_cell(struct cell_manager *pthis, const struct tag_hash_key *tag)
-{
- if (pthis->sampling_mode == SAMPLING_MODE_TOPK) {
- return -1;
- }
-
- struct tag_id_map *node = NULL;
- HASH_FIND_TAG(pthis->comprehensive_tag_id_map, tag, node);
- if (node == NULL) {
- return -1;
- }
-
- int cell_id = node->cell_id;
-
- HASH_DEL(pthis->comprehensive_tag_id_map, node);
- tag_hash_key_free(node->tag);
- free(node);
- pthis->id_tag_array[cell_id] = NULL;
-
- return cell_id;
-}
-
-struct cell_manager *cell_manager_copy(const struct cell_manager *src)
-{
- struct cell_manager *pthis = cell_manager_new_without_map(src->sampling_mode, src->max_cell_num);
- pthis->next_cell_id = src->next_cell_id;
-
- if (src->sampling_mode == SAMPLING_MODE_TOPK) {
- pthis->topk_tag_id_map = heavy_keeper_copy(src->topk_tag_id_map);
-
- struct heavy_keeper_result *result = heavy_keeper_query(pthis->topk_tag_id_map);
- for (size_t i = 0; i < result->n_key; i++) {
- cell_manager_id_tag_array_add(pthis, result->cell_id[i], result->tags[i]);
- }
- heavy_keeper_result_free(result);
- } else {
- for (int i = 0; i <= src->max_cell_id; i++) {
- const struct tag_hash_key *tag = src->id_tag_array[i];
- if (tag == NULL) {
- continue;
- }
- struct tag_hash_key *tag_copy = tag_hash_key_copy(tag);
- struct tag_id_map *tmp_node = (struct tag_id_map *)malloc(sizeof(struct tag_id_map));
- tmp_node->tag = tag_copy;
- tmp_node->cell_id = i;
- tmp_node->dying = false;
- HASH_ADD_TAG(pthis->comprehensive_tag_id_map, tag, tmp_node);
-
- cell_manager_id_tag_array_add(pthis, i, tag_copy);
- }
- }
-
- return pthis;
-}
-
-const struct tag_hash_key **cell_manager_dump(const struct cell_manager *pthis, int *array_len)
-{
- *array_len = pthis->max_cell_id + 1;
-
- return (const struct tag_hash_key **)pthis->id_tag_array;
-}
-
-int cell_manager_find(const struct cell_manager *pthis, const struct tag_hash_key *tag)
-{
- if (pthis->sampling_mode == SAMPLING_MODE_COMPREHENSIVE) {
- struct tag_id_map *node = NULL;
- HASH_FIND_TAG(pthis->comprehensive_tag_id_map, tag, node);
- if (node == NULL) {
- return -1;
- } else {
- return node->dying ? -1 : node->cell_id;
- }
- } else {
- int cell_id = -1;
- heavy_keeper_query_one(pthis->topk_tag_id_map, tag, NULL, &cell_id);
- return cell_id;
- }
-}
-
-void cell_manager_merge_topk(struct cell_manager *dest, const struct cell_manager *src,
- int **cell_id_popped, int *n_cell_id_popped, int **cell_id_old, int **cell_id_added, int *n_cell_id_added)
-{
- heavy_keeper_merge_recording_id_details(dest->topk_tag_id_map, src->topk_tag_id_map,
- cell_id_popped, n_cell_id_popped, cell_id_old, cell_id_added, n_cell_id_added);
-
- memset(dest->id_tag_array, 0, sizeof(struct tag_hash_key *) * dest->id_tag_array_len);
- struct heavy_keeper_result *result = heavy_keeper_query(dest->topk_tag_id_map);
- for (size_t i = 0; i < result->n_key; i++) {
- cell_manager_id_tag_array_add(dest, result->cell_id[i], result->tags[i]);
- }
- heavy_keeper_result_free(result);
-}
-
-int cell_manager_get_cardinality(const struct cell_manager *pthis)
-{
- if (pthis->sampling_mode == SAMPLING_MODE_COMPREHENSIVE) {
- return pthis->current_cell_num;
- } else {
- return heavy_keeper_get_cardinality(pthis->topk_tag_id_map);
- }
-}
-
-void cube_manager_free(struct cube_manager *pthis)
-{
- tag_id_map_free(pthis->head);
- free(pthis);
-}
-
-struct cube_manager *cube_manager_new()
-{
- struct cube_manager *pthis = (struct cube_manager *)malloc(sizeof(struct cube_manager));
- pthis->head = NULL;
- return pthis;
-}
-
-void cube_manager_add(struct cube_manager *pthis, const struct tag_hash_key *tag, int id)
-{
- struct tag_id_map *node = (struct tag_id_map *)malloc(sizeof(struct tag_id_map));
- node->tag = tag_hash_key_copy(tag);
- node->cell_id = id;
- node->dying = false; // this is not used in cube manager, just give it a value
- HASH_ADD_TAG(pthis->head, tag, node);
-}
-
-void cube_manager_delete(struct cube_manager *pthis, const struct tag_hash_key *tag)
-{
- struct tag_id_map *node = NULL;
- HASH_FIND_TAG(pthis->head, tag, node);
- if (node != NULL) {
- HASH_DEL(pthis->head, node);
- tag_hash_key_free(node->tag);
- free(node);
- }
-}
-
-int cube_manager_find(const struct cube_manager *pthis, const struct tag_hash_key *tag)
-{
- struct tag_id_map *node = NULL;
- HASH_FIND_TAG(pthis->head, tag, node);
- if (node == NULL) {
- return -1;
- } else {
- return node->cell_id;
- }
-}
-
-void cube_manager_calibrate(struct cube_manager *pthis, const struct cube_manager *master)
-{
- struct tag_id_map *node = NULL;
- struct tag_id_map *tmp = NULL;
- HASH_ITER(hh, pthis->head, node, tmp) {
- int cube_id = cube_manager_find(master, node->tag);
- if (cube_id == -1) {
- HASH_DEL(pthis->head, node);
- tag_hash_key_free(node->tag);
- free(node);
- } else {
- node->cell_id = cube_id;
- }
- }
-
- // exist in master but not in self
- HASH_ITER(hh, master->head, node, tmp) {
- int cube_id = cube_manager_find(pthis, node->tag);
- if (cube_id == -1) {
- cube_manager_add(pthis, node->tag, node->cell_id);
- }
- }
-} \ No newline at end of file
diff --git a/src/tags/cell_manager.h b/src/tags/cell_manager.h
deleted file mode 100644
index 479e444..0000000
--- a/src/tags/cell_manager.h
+++ /dev/null
@@ -1,41 +0,0 @@
-
-#pragma once
-#include "my_ut_hash.h"
-
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-struct cell_manager;
-
-void cell_manager_free(struct cell_manager *pthis);
-struct cell_manager *cell_manager_new(enum sampling_mode sampling_mode, int max_cell_num);
-struct cell_manager *cell_manager_copy(const struct cell_manager *src);
-void cell_manager_reset(struct cell_manager *pthis);
-
-const struct tag_hash_key *cell_manager_get_tag_by_cell_id(const struct cell_manager *pthis, int cell_id);
-long long cell_manager_get_count_by_tag(const struct cell_manager *pthis, const struct tag_hash_key *tag);
-const struct tag_hash_key **cell_manager_dump(const struct cell_manager *pthis, int *array_len);
-int cell_manager_find(const struct cell_manager *pthis, const struct tag_hash_key *tag);
-int cell_manager_get_cardinality(const struct cell_manager *pthis);
-
-int cell_manager_add_cell(struct cell_manager *pthis, const struct tag_hash_key *tag);
-int cell_manager_add_cell_topk(struct cell_manager *pthis, const struct tag_hash_key *tag, unsigned long long int count, int *popped_cell_id);
-int cell_manager_delete_cell(struct cell_manager *pthis, const struct tag_hash_key *tag);
-
-void cell_manager_merge_topk(struct cell_manager *dest, const struct cell_manager *src,
- int **cell_id_popped, int *n_cell_id_popped, int **cell_id_old, int **cell_id_added, int *n_cell_id_added);
-
-/* ------- a simpler cell_manager, which only support check if tag exist and add. ------- */
-struct cube_manager;
-
-struct cube_manager *cube_manager_new();
-void cube_manager_free(struct cube_manager *pthis);
-void cube_manager_add(struct cube_manager *pthis, const struct tag_hash_key *tag, int id);
-int cube_manager_find(const struct cube_manager *pthis, const struct tag_hash_key *tag);
-void cube_manager_delete(struct cube_manager *pthis, const struct tag_hash_key *tag);
-void cube_manager_calibrate(struct cube_manager *pthis, const struct cube_manager *master);
-
-#ifdef __cplusplus
-}
-#endif \ No newline at end of file
diff --git a/src/tags/heavy_keeper.c b/src/tags/heavy_keeper.c
deleted file mode 100644
index b733dc6..0000000
--- a/src/tags/heavy_keeper.c
+++ /dev/null
@@ -1,563 +0,0 @@
-#include "heavy_keeper.h"
-
-#include <float.h>
-#include <limits.h>
-#include <math.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include "minheap/heap.h"
-#include "mpack/mpack.h"
-#include "xxhash/xxhash.h"
-#include "cjson/cJSON.h"
-
-#include "tags/sorted_set.h"
-#include "tags/my_ut_hash.h"
-
-
-struct Bucket {
- unsigned int finger_print;
- unsigned long long count; // the actual count, sum of all uuid_hash_node->count
-};
-
-struct heavy_keeper {
- int K;
- struct sorted_set *top_K_heap;
- struct heavy_keeper_options params;
- struct Bucket *sketch;
-
- unsigned int rand_state;
-};
-
-#define FP_HASH_KEY 0
-
-struct Bucket *new_sketch(struct heavy_keeper_options *params) {
- size_t array_len = (size_t)params->array_num * (size_t)params->max_bucket_num;
-
- struct Bucket *ret = (struct Bucket *)calloc(array_len, sizeof(struct Bucket));
-
- return ret;
-}
-
-void params_set_to_default(struct heavy_keeper_options *p, int K) {
- if (K > 1000) { // don't let the sketch too large when K gets insanely large
- K = 1000;
- }
- double log_ret = log((double)K) / 2.3; // 2.3: log_e(10), log_ret = log_10(K)
-
- if (log_ret < 3) {
- p->array_num = 3;
- } else {
- p->array_num = (int)(log_ret);
- }
- p->decay_exp_rate = 1.17; // by test, 1.17 is the best
- p->max_bucket_num = (int)(log_ret * K * 2);
- if (p->max_bucket_num < 150) {
- p->max_bucket_num = 150; // determined through test, too small max_bucket_num will let accuracy decrease severely.
- } else if (p->max_bucket_num > 600) {
- p->max_bucket_num = p->max_bucket_num / 4 + 450;
- }
-}
-
-void params_set_to(struct heavy_keeper_options *p, const struct heavy_keeper_options *src) {
- if (src->array_num != 0) {
- p->array_num = src->array_num;
- }
- if (src->decay_exp_rate != 0) {
- p->decay_exp_rate = src->decay_exp_rate;
- }
- if (src->max_bucket_num != 0) {
- p->max_bucket_num = src->max_bucket_num;
- }
-}
-
-struct heavy_keeper *heavy_keeper_new(int max_query_num, const struct heavy_keeper_options *params) {
- struct heavy_keeper *hk = (struct heavy_keeper *)malloc(sizeof(struct heavy_keeper));
- hk->K = max_query_num;
- hk->rand_state = 0;
-
- hk->top_K_heap = sorted_set_new(max_query_num);
-
- params_set_to_default(&(hk->params), max_query_num);
-
- if (params != NULL) {
- params_set_to(&(hk->params), params);
- }
- hk->sketch = new_sketch(&(hk->params));
- return hk;
-}
-
-struct heavy_keeper *heavy_keeper_with_data_new(int max_query_num) {
- struct heavy_keeper *hk = heavy_keeper_new(max_query_num, NULL);
- return hk;
-}
-
-void heavy_keeper_reset(struct heavy_keeper *hk) {
- memset(hk->sketch, 0, (size_t)hk->params.array_num * (size_t)hk->params.max_bucket_num * sizeof(struct Bucket));
- sorted_set_reset(hk->top_K_heap);
-}
-
-void heavy_keeper_result_free(struct heavy_keeper_result *stats_hd) {
- free(stats_hd->count);
- free(stats_hd->tags);
- free(stats_hd->cell_id);
- free(stats_hd);
-}
-
-void heavy_keeper_free(struct heavy_keeper *hk) {
- sorted_set_free(hk->top_K_heap);
- free(hk->sketch);
- free(hk);
-}
-
-/*********************************add and query*************************************/
-/***********************************************************************************/
-const double DECAY_POW_TABLE[147] = { // 1.17 ^ exp, exp is in [0,146]
- 1.0,0.8547008547009,0.7305135510264,0.6243705564328,0.5336500482332,0.456111152336,0.3898385917402,0.3331953775557,0.2847823739793,0.2434037384438,0.2080373832853,0.1778097293037,0.15197412761,
- 0.1298924167607,0.1110191596245,0.0948881706192,0.0811010005293,0.0693170944695,0.0592453798884,0.0506370768277,0.0432795528442,0.036991070807,0.031616299835,0.0270224784915,0.0230961354628,0.0197402867204,
- 0.0168720399319,0.0144205469504,0.0123252538037,0.0105344049605,0.0090037649235,0.0076955255756,0.0065773722868,0.0056216857153,0.0048048595857,0.0041067175946,0.0035100150381,0.0030000128531,0.0025641135497,
- 0.0021915500424,0.0018731196944,0.0016009570038,0.0013683393194,0.0011695207859,0.0009995904153,0.0008543507823,0.0007302143438,0.0006241148238,0.0005334314733,0.0004559243362,0.0003896789198,0.0003330589058,
- 0.0002846657315,0.000243304044,0.0002079521743,0.0001777369012,0.0001519118813,0.0001298392148,0.0001109736879,0.0000948493059,0.0000810677828,0.0000692887032,0.0000592211139,0.0000506163367,0.0000432618262,
- 0.0000369759198,0.0000316033503,0.0000270114105,0.0000230866756,0.0000197322014,0.0000168651294,0.0000144146405,0.0000123202056,0.0000105300902,0.0000090000771,0.0000076923736,0.0000065746783,0.0000056193832,
- 0.0000048028916,0.0000041050355,0.0000035085774,0.0000029987841,0.0000025630633,0.0000021906524,0.0000018723525,0.0000016003013,0.0000013677789,0.0000011690418,0.000000999181,0.0000008540009,0.0000007299153,
- 0.0000006238592,0.000000533213,0.0000004557376,0.0000003895193,0.0000003329225,0.0000002845491,0.0000002432044,0.000000207867,0.0000001776641,0.0000001518497,0.000000129786,0.0000001109282,0.0000000948105,
- 0.0000000810346,0.0000000692603,0.0000000591969,0.0000000505956,0.0000000432441,0.0000000369608,0.0000000315904,0.0000000270003,0.0000000230772,0.0000000197241,0.0000000168582,0.0000000144087,0.0000000123152,
- 0.0000000105258,0.0000000089964,0.0000000076892,0.000000006572,0.0000000056171,0.0000000048009,0.0000000041034,0.0000000035071,0.0000000029976,0.000000002562,0.0000000021898,0.0000000018716,0.0000000015996,
- 0.0000000013672,0.0000000011686,0.0000000009988,0.0000000008537,0.0000000007296,0.0000000006236,0.000000000533,0.0000000004556,0.0000000003894,0.0000000003328,0.0000000002844,0.0000000002431,0.0000000002078,
- 0.0000000001776,0.0000000001518,0.0000000001297,0.0000000001109
-};
-
-bool if_need_to_decay(struct heavy_keeper *hk, const struct Bucket *bucket, unsigned long long count) {
- if (count == 0) {
- return false;
- }
- if (bucket->count < count) { // the exp is 0, so the decay rate is 1
- return true;
- }
-
- unsigned long long exp = bucket->count / count;
- if (exp > 146) { // 1.17 ^ 146 < 1 / RAND_MAX < rand_r / RAND_MAX, so there is no chance to decay
- return false;
- }
-
- double r = (double)rand_r(&(hk->rand_state)) / (double)RAND_MAX;
-
- // double b = hk->params.decay_exp_rate;
- // double decay_rate = pow(b, -exp);
- // p->decay_exp_rate = 1.17 is fixed, search table to get result directly.
- double decay_rate = DECAY_POW_TABLE[exp];
-
- if (r < decay_rate) {
- return true;
- }
- return false;
-}
-
-struct Bucket *map_flow_id_hash_to_bucket(struct heavy_keeper *hk, int array_index, const struct tag_hash_key *tag) {
- int w = hk->params.max_bucket_num;
- int Hsh = tag_hash_key_cal_hash_val(tag, array_index + 1) % w; // +1: the let any row do not use FP as hashing key directly.
- // Otherwise, when different key has the same FP(in 2^-64 probability), they will also in the same bucket in the row #0 and hash collision will happen more severely.
-
- return &(hk->sketch[array_index * w + Hsh]);
-}
-
-unsigned long long count_add(unsigned long long a, unsigned long long b) {
- if (a > UINT64_MAX - b) {
- return UINT64_MAX;
- }
- return a + b;
-}
-
-unsigned my_max(unsigned a, unsigned b) {
- if (a > b) {
- return a;
- }
- return b;
-}
-
-static int heavy_keeper_add_by_recording_popped_data(struct heavy_keeper *heavy_keeper, const struct tag_hash_key *tag, unsigned long long int count, int cell_id, int *popped_id_out)
-{
- if (count == 0) {
- int cell_id_old = sorted_set_get_cell_id(heavy_keeper->top_K_heap, tag);
- if (cell_id_old > 0) {
- return 1; // the flow is in the sorted set. Use original cell id.
- }
-
- assert(cell_id >= 0);
- assert(tag != NULL);
- long long tmp_v = tag->hashv;
- assert(tmp_v >= 0);
-
- if (sorted_set_cardinality(heavy_keeper->top_K_heap) < heavy_keeper->K) {
- sorted_set_insert_and_record_popped_back(heavy_keeper->top_K_heap, tag, 0, cell_id, popped_id_out);
- return 0; // newly added
- }
- return -1; // not added
- }
-
- int user_data_cube_id = 0;
- struct sorted_set *summary = heavy_keeper->top_K_heap;
-
- unsigned nMin = sorted_set_get_min_count(summary);
- if (nMin == INVALID_COUNT) {
- nMin = 0;
- }
- unsigned long long int old_cnt = sorted_set_get_count(summary, tag);
- bool not_in_sorted_set = (old_cnt == INVALID_COUNT);
- unsigned long long maxv = 0;
-
- unsigned int FP = tag_hash_key_cal_hash_val(tag, FP_HASH_KEY);
- for (int j = 0; j < heavy_keeper->params.array_num; j++) {
- struct Bucket *bucket = map_flow_id_hash_to_bucket(heavy_keeper, j, tag);
-
- if (bucket->finger_print == FP) {
- // If a flow is not in the min-heap, then the estimated flow size should be no larger than nmin.
- // The flows whose counts are both larger than nmin and not in min-heap must have the same xxhash value, and its FP stored in bucket represents another tag,
- // In this case, the sketch won't be updated. This flow is expected to be taken into account in another array,
- // where its FP is different from the one it should collided with, so that element flows won't be missed.
- if (bucket->count > nMin && not_in_sorted_set) {
- continue;
- }
- bucket->count = count_add(bucket->count, count);
- maxv = my_max(maxv, bucket->count);
- } else {
- if (!if_need_to_decay(heavy_keeper, bucket, count)) {
- continue;
- }
- if (bucket->count < count) {
- bucket->finger_print = FP;
- bucket->count = count;
-
- maxv = my_max(maxv, count);
- } else {
- bucket->count -= count;
- }
- }
- }
-
-
- if (not_in_sorted_set) {
- if ((maxv - nMin <= count && maxv != nMin) || sorted_set_cardinality(summary) != heavy_keeper->K) {
- assert(cell_id >= 0);
- assert(tag != NULL);
- long long tmp_v = tag->hashv;
- assert(tmp_v >= 0);
-
- int insert_ret = sorted_set_insert_and_record_popped_back(summary, tag, maxv, cell_id, &user_data_cube_id);
- if (insert_ret == 2) { // insert success and popped one
- *popped_id_out = user_data_cube_id;
- return 0;
- } else if (insert_ret == 0) { // insert failed
- return -1;
- } else if (insert_ret == 1) { // insert success without popping
- return 0;
- } else {
- return 2;
- }
- }
- return -1; // the flow is not added to the sorted set
- } else {
- if (maxv > old_cnt) {
- sorted_set_incrby(summary, tag, maxv - old_cnt);
- }
- return 1; // no popped, but the tag definitely exists in the sorted set
- }
-
- // should not reach here
- return -1;
-}
-
-int heavy_keeper_add(struct heavy_keeper *hk, const struct tag_hash_key *tag, unsigned long long int count, int cell_id, int *popped_id_out)
-{
- *popped_id_out = -1;
- heavy_keeper_add_by_recording_popped_data(hk, tag, count, cell_id, popped_id_out);
-
- int cell_id_tmp = sorted_set_get_cell_id(hk->top_K_heap, tag);
- if (cell_id_tmp >= 0) {
- if (cell_id_tmp == cell_id) { // insert success
- return 0;
- } else {
- return 1; // insert success, but use the different cell_id
- }
- } else {
- return -1;
- }
-
- return -1; // should not reach here
-}
-
-struct tag_hash_key *heavy_keeper_get_true_pst_tag(const struct heavy_keeper *hk, const struct tag_hash_key *tag)
-{
- return sorted_set_get_true_pst_tag(hk->top_K_heap, tag);
-}
-
-struct heavy_keeper_result *heavy_keeper_query(const struct heavy_keeper *hk) {
- size_t query_entry_num = sorted_set_cardinality(hk->top_K_heap);
- struct heavy_keeper_result *stats = (struct heavy_keeper_result *)malloc(sizeof(struct heavy_keeper_result));
- stats->count = (unsigned long long *)malloc(query_entry_num * sizeof(unsigned long long));
- stats->tags = (struct tag_hash_key **)malloc(query_entry_num * sizeof(struct tag_hash_key *));
- stats->cell_id = (int *)malloc(query_entry_num * sizeof(int));
- stats->n_key = query_entry_num;
-
- sorted_set_dump(hk->top_K_heap, stats->count, stats->tags, stats->cell_id);
-
- return stats;
-}
-
-size_t heavy_keeper_get_cardinality(const struct heavy_keeper *hk) {
- return sorted_set_cardinality(hk->top_K_heap);
-}
-
-int heavy_keeper_query_one(const struct heavy_keeper *hk, const struct tag_hash_key *tag, unsigned long long int *count_out, int *cell_id_out)
-{
- if (count_out != NULL) {
- unsigned long long int count = sorted_set_get_count(hk->top_K_heap, tag);
- if (count == INVALID_COUNT) {
- *count_out = 0;
- return -1;
- }
- *count_out = count;
- }
-
- if (cell_id_out == NULL) {
- return 0;
- }
-
- int cell_id = sorted_set_get_cell_id(hk->top_K_heap, tag);
- if (cell_id == -1) {
- *cell_id_out = -1;
- return -1;
- }
- *cell_id_out = cell_id;
-
- return 0;
-}
-
-/*****************************************merge *****************************************/
-/****************************************************************************************/
-static void heavy_keeper_merge_sketch(struct heavy_keeper *dest, const struct heavy_keeper *src) {
- int w = dest->params.max_bucket_num;
- int d = dest->params.array_num;
- //idx
- for (int array_id = 0; array_id < d; array_id++) {
- for (int bucket_id = 0; bucket_id < w; bucket_id++) {
- struct Bucket *bucket_dest = &(dest->sketch[array_id * w + bucket_id]);
- const struct Bucket *bucket_src = &(src->sketch[array_id * w + bucket_id]);
-
- if (bucket_dest->finger_print == bucket_src->finger_print) {
- bucket_dest->count = count_add(bucket_dest->count, bucket_src->count);
- } else {
- if (bucket_dest->count < bucket_src->count) { // bucket_src stores the elephant flow.
- bucket_dest->count = bucket_src->count;
- bucket_dest->finger_print = bucket_src->finger_print;
- }
- }
- }
- }
-}
-
-unsigned long long find_maxv_in_sketch(struct heavy_keeper *hk, const struct tag_hash_key *tag) {
- struct Bucket *bucket;
- unsigned fp = tag_hash_key_cal_hash_val(tag, FP_HASH_KEY);
-
- unsigned long long maxv = 0;
- for (int array_id = 0; array_id < hk->params.array_num; array_id++) {
- bucket = map_flow_id_hash_to_bucket(hk, array_id, tag); // hk->sketch is the merge of two. So just check one
-
- if (bucket->finger_print == fp) {
- maxv = my_max(maxv, bucket->count);
- }
- }
-
- return maxv;
-}
-
-int find_next_unused_cell_id(const int *sorted_cell_id_arr, size_t arr_len, int last_find_result, int *next_idx)
-{
- int cur_idx = *next_idx;
-
- if (arr_len == 0) {
- if (last_find_result < 0) {
- return 0;
- } else {
- return last_find_result + 1;
- }
- }
-
- if (cur_idx >= arr_len) {
- int max_id = sorted_cell_id_arr[arr_len - 1];
- if (last_find_result <= max_id) { // this first time reach the end of sorted_pst_cell_id_arr
- return max_id + 1;
- } else {
- return last_find_result + 1;
- }
- }
-
- int expected_id = last_find_result + 1;
- while (cur_idx < arr_len) {
- int cur_id = sorted_cell_id_arr[cur_idx];
-
- // check if expected_id id is used
- if (cur_id == expected_id) { // used
- expected_id++;
- cur_idx++;
- } else if (cur_id > expected_id) { // not used
- *next_idx = cur_idx;
- return expected_id;
- } else { // still cannot guarantee expected_id is not used
- cur_idx++;
- }
- }
-
- // all cell_id in sorted_pst_cell_id_arr is used
- int max_id = sorted_cell_id_arr[arr_len - 1];
- *next_idx = arr_len;
- return max_id + 1;
-}
-
-int cmp_int(const void *a, const void *b) {
- return *(int *)a - *(int *)b;
-}
-
-void heavy_keeper_merge_sorted_set_recording_id_details(struct heavy_keeper *dest, const struct heavy_keeper *src,
- int **cell_id_popped_from_dest_when_merge_src_out, int *cell_id_popped_from_dest_when_merge_src_len_out,
- int **cell_id_in_src_before_merge_out, int **cell_id_in_dest_corresponding_out, int *cell_id_in_src_before_merge_len_out)
-{
- struct sorted_set *new_rec = sorted_set_new(dest->K); // merging result
-
- struct sorted_set *ss_dest = dest->top_K_heap; // original ss in dest
- const struct sorted_set *ss_src = src->top_K_heap; // the ss to be merged
- int size_dest = sorted_set_cardinality(ss_dest);
- int size_src = sorted_set_cardinality(ss_src);
- int max_size = size_dest > size_src ? size_dest : size_src;
-
- unsigned long long *count_arr = (unsigned long long *)malloc(max_size * sizeof(unsigned long long));
- // struct tag_hash_key **tag_arr = (struct tag_hash_key **)malloc(max_size * sizeof(struct tag_hash_key *));
- struct tag_hash_key **tag_arr = (struct tag_hash_key **)calloc(max_size, sizeof(struct tag_hash_key *));
- int *cell_id_arr_dst = (int *)malloc(size_dest * sizeof(int));
-
- sorted_set_dump(ss_dest, count_arr, tag_arr, cell_id_arr_dst);
- int cell_id_popped_from_dest_when_merge_src[dest->K];
- int cell_id_popped_from_dest_when_merge_src_len = 0;
- int cell_id_in_src_before_merge[dest->K];
- int cell_id_in_src_before_merge_len = 0;
- int cell_id_in_dest_corresponding[dest->K]; // length == cell_id_in_src_before_merge_len
-
- /* ------------------------------ merge dest ------------------------------ */
- for (int i = 0; i < size_dest; i++) {
- unsigned long long maxv = find_maxv_in_sketch(dest, tag_arr[i]);
- assert(tag_arr[i] != NULL);
- assert(cell_id_arr_dst[i] >= 0);
- sorted_set_insert(new_rec, tag_arr[i], maxv, cell_id_arr_dst[i]);
- }
-
- qsort(cell_id_arr_dst, size_dest, sizeof(int), cmp_int); // use in find_next_unused_cell_id to find the minimum unused cell_id
-
- /* ------------------------------ merge source ------------------------------ */
- // int *cell_id_arr_src = (int *)malloc(size_src * sizeof(int));
- int *cell_id_arr_src = (int*)calloc(size_src, sizeof(int));
- sorted_set_dump(ss_src, count_arr, tag_arr, cell_id_arr_src);
- int last_find_id = -1;
- int next_idx = 0;
-
- for (int i = 0; i < size_src; i++) {
- const struct tag_hash_key *tag_src = tag_arr[i];
- assert(tag_src != NULL);
-
- int cell_id_dest = sorted_set_get_cell_id(ss_dest, tag_src);
- int cell_id_src = cell_id_arr_src[i];
-
- if (cell_id_dest != -1) { // the tag is in both dest and src, so has been processed in the previous loop. The reason why no need to sum up result is that merging sketch already gets its summed up count
- cell_id_in_src_before_merge[cell_id_in_src_before_merge_len] = cell_id_src;
- cell_id_in_dest_corresponding[cell_id_in_src_before_merge_len] = cell_id_dest;
- cell_id_in_src_before_merge_len++;
-
- continue;
- }
-
- unsigned long long maxv = find_maxv_in_sketch(dest, tag_src); // the dest heavy keeper has been merged, so the maxv is the maxv in the merged sketch, instead of the dest sketch
- int popped_id = -1;
- int now_idx = next_idx;
- int find_id = find_next_unused_cell_id(cell_id_arr_dst, size_dest, last_find_id, &now_idx);
- assert(find_id >= 0);
- assert(now_idx >= 0);
- int tmp_ret = sorted_set_insert_and_record_popped_back(new_rec, tag_src, maxv, find_id, &popped_id);
- if (tmp_ret == 2) { // insert success and popped one.
- cell_id_popped_from_dest_when_merge_src[cell_id_popped_from_dest_when_merge_src_len++] = popped_id;
- }
- if (tmp_ret != 0) { // insert success
- last_find_id = find_id;
- next_idx = now_idx;
-
- cell_id_in_src_before_merge[cell_id_in_src_before_merge_len] = cell_id_src;
- cell_id_in_dest_corresponding[cell_id_in_src_before_merge_len] = last_find_id;
- cell_id_in_src_before_merge_len++;
- }
- }
-
- free(count_arr);
- free(tag_arr);
- free(cell_id_arr_dst);
- free(cell_id_arr_src);
- sorted_set_free(ss_dest);
-
- dest->top_K_heap = new_rec;
-
- if (cell_id_popped_from_dest_when_merge_src_len == 0) {
- *cell_id_popped_from_dest_when_merge_src_out = NULL;
- *cell_id_popped_from_dest_when_merge_src_len_out = 0;
- } else {
- *cell_id_popped_from_dest_when_merge_src_out = (int *)malloc(cell_id_popped_from_dest_when_merge_src_len * sizeof(int));
- memcpy(*cell_id_popped_from_dest_when_merge_src_out, cell_id_popped_from_dest_when_merge_src, cell_id_popped_from_dest_when_merge_src_len * sizeof(int));
- *cell_id_popped_from_dest_when_merge_src_len_out = cell_id_popped_from_dest_when_merge_src_len;
- }
- if (cell_id_in_src_before_merge_len == 0) {
- *cell_id_in_src_before_merge_out = NULL;
- *cell_id_in_src_before_merge_len_out = 0;
- *cell_id_in_dest_corresponding_out = NULL;
- } else {
- *cell_id_in_src_before_merge_out = (int *)malloc(cell_id_in_src_before_merge_len * sizeof(int));
- memcpy(*cell_id_in_src_before_merge_out, cell_id_in_src_before_merge, cell_id_in_src_before_merge_len * sizeof(int));
- *cell_id_in_src_before_merge_len_out = cell_id_in_src_before_merge_len;
- *cell_id_in_dest_corresponding_out = (int *)malloc(cell_id_in_src_before_merge_len * sizeof(int));
- memcpy(*cell_id_in_dest_corresponding_out, cell_id_in_dest_corresponding, cell_id_in_src_before_merge_len * sizeof(int));
- }
-}
-
-int heavy_keeper_merge_recording_id_details(struct heavy_keeper *dest, const struct heavy_keeper *src,
- int **cell_id_popped_from_dest_when_merge_src_out, int *cell_id_popped_from_dest_when_merge_src_len_out,
- int **cell_id_in_src_before_merge_out, int **cell_id_in_dest_corresponding_out, int *cell_id_in_src_before_merge_len_out)
-{
- if (dest->params.array_num != src->params.array_num ||
- dest->params.max_bucket_num != src->params.max_bucket_num ||
- dest->K != src->K) {
- return -1;
- }
-
- heavy_keeper_merge_sketch(dest, src);
- heavy_keeper_merge_sorted_set_recording_id_details(dest, src,
- cell_id_popped_from_dest_when_merge_src_out, cell_id_popped_from_dest_when_merge_src_len_out,
- cell_id_in_src_before_merge_out, cell_id_in_dest_corresponding_out, cell_id_in_src_before_merge_len_out);
- return 0;
-}
-
-struct heavy_keeper *heavy_keeper_copy(const struct heavy_keeper *src)
-{
- struct heavy_keeper *hk = (struct heavy_keeper *)malloc(sizeof(struct heavy_keeper));
- hk->params = src->params;
- hk->K = src->K;
- hk->sketch = (struct Bucket *)malloc(hk->params.array_num * hk->params.max_bucket_num * sizeof(struct Bucket));
- memcpy(hk->sketch, src->sketch, hk->params.array_num * hk->params.max_bucket_num * sizeof(struct Bucket));
-
- hk->top_K_heap = sorted_set_copy(src->top_K_heap);
- return hk;
-}
-
-int heavy_keeper_get_config_K(const struct heavy_keeper *hk)
-{
- return hk->K;
-}
diff --git a/src/tags/heavy_keeper.h b/src/tags/heavy_keeper.h
deleted file mode 100644
index 688911b..0000000
--- a/src/tags/heavy_keeper.h
+++ /dev/null
@@ -1,110 +0,0 @@
-#pragma once
-
-#include <stddef.h>
-#include <uuid/uuid.h>
-
-/* ---------------------------------------------------------------------------------------
-TOP K using heavykeeper algorithm.
-Author: Chen Zizhan
-Time: 2023/4/26
----------------------------------------------------------------------------------------------*/
-
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-
-// Query result
-struct heavy_keeper_result {
- size_t n_key; // number of result
- struct tag_hash_key **tags;
- unsigned long long *count; // count[i] is the metric count of the corresponding flow id
-
- int *cell_id;
-};
-
-// Parameters used in algorithm
-struct heavy_keeper_options{
- int array_num; // the size of the array. Default value: 4
- // Set it by how accurate you want. Value of 4 guarantees an accuracy more than 90% and around 97% in all tests.
- // Not too big because it have an impact on both time and memory efficiency.
- int max_bucket_num; // M2, the maximum number of buckets every array keeps. Default value: k*log(k) and no less than 100.
- // Basically, as long as big enough, it won't affect the accuracy significantly.
- double decay_exp_rate; // b, bigger variance of flow size is(elephant flows take more ratio), smaller it should be.
- // Must bigger than 1. Better not bigger than 1.3, otherwise some elephant flow will be missed.
-};
-
-struct heavy_keeper;
-
-struct heavy_keeper *heavy_keeper_with_data_new(int max_query_num);
-/**
- * @brief free a heavy keeper.
- * @param hk the pointer to the heavy keeper.
- */
-void heavy_keeper_free(struct heavy_keeper *hk);
-
-/*
- * @brief clear a heavy keeper. It will clear all the data in the heavy keeper.
- * @param hk the pointer to the heavy keeper.
-*/
-void heavy_keeper_reset(struct heavy_keeper *hk);
-
-// tag: the pointer to the tag, user should alloc it. Its memory will be freed by heavy keeper, so don't free it after calling heavy_keeper_add.
-// return -1 when the key is not inserted to the heavy keeper(not fail). popped_id_out is -1 if not popped. any other value >= 0 means the id of the popped member.
-// the cube id will give the key if the key is new, in which case return 0, if the key is already in the heavy keeper, return 1.
-int heavy_keeper_add(struct heavy_keeper *hk, const struct tag_hash_key *tag, unsigned long long int count, int cube_id, int *popped_id_out);
-
-/**
- * @brief Query the top-K flows.The flow id with bigger count ranks at smaller index.
- * @param hk the pointer to the heavy keeper.
- * @return a pointer to the heavy keeper query result. User should free it after use by calling heavy_keeper_result_free.
- */
-struct heavy_keeper_result *heavy_keeper_query(const struct heavy_keeper *hk);
-
-int heavy_keeper_query_one(const struct heavy_keeper *hk, const struct tag_hash_key *tag, unsigned long long int *count_out, int *cube_id_out);
-
-size_t heavy_keeper_get_cardinality(const struct heavy_keeper *hk);
-/**
- * @brief free a heavy keeper query result.
- * @param stats_hd the pointer to the heavy keeper query result.
- */
-void heavy_keeper_result_free(struct heavy_keeper_result *stats_hd);
-
-int heavy_keeper_merge_recording_id_details(struct heavy_keeper *dest, const struct heavy_keeper *src,
- int **cell_id_popped_from_dest_when_merge_src_out, int *cell_id_popped_from_dest_when_merge_src_len_out,
- int **cell_id_in_src_before_merge_out, int **cell_id_in_dest_corresponding_out, int *cell_id_in_src_before_merge_len_out);
-
-/*
- Equal to heavy_keeper_merge, with empty dest, but much faster.
-*/
-struct heavy_keeper *heavy_keeper_copy(const struct heavy_keeper *src);
-
-/**
- * @brief Serialize a heavy keeper.
- * @param hk the pointer to the heavy keeper.
- * @param blob output of the serialized data. Must be freed by caller. *blob can be NULL, the function will allocate memory for it.
- * @param blob_sz Output, the size of the serialized data.
-*/
-void heavy_keeper_serialization(const struct heavy_keeper *hk, char **blob, size_t *blob_sz);
-
-/**
- * @brief Deserialize a heavy keeper.
- * @param blob the serialized data. From heavy_keeper_serialization.
- * @param size the size of the serialized data.
- * @returns a pointer to the heavy keeper.
-*/
-struct heavy_keeper *heavy_keeper_deserialization(const char *blob, size_t size);
-
-
-/**
- * @brief Get the "K" parameter of the heavy keeper.
-*/
-int heavy_keeper_get_config_K(const struct heavy_keeper *hk);
-
-void heavy_keeper_get_config_metric_header(struct heavy_keeper *hk, char **metric_names, size_t *n_metric, size_t *primary_metric_index);
-
-struct tag_hash_key *heavy_keeper_get_true_pst_tag(const struct heavy_keeper *hk, const struct tag_hash_key *tag);
-
-#ifdef __cplusplus
-}
-#endif \ No newline at end of file
diff --git a/src/tags/my_ut_hash.c b/src/tags/my_ut_hash.c
deleted file mode 100644
index 8123456..0000000
--- a/src/tags/my_ut_hash.c
+++ /dev/null
@@ -1,467 +0,0 @@
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-#define XXH_INLINE_ALL
-#include "xxhash/xxhash.h"
-#include "my_ut_hash_inner.h"
-#include "my_ut_hash.h"
-#include "serializer.h"
-
-
-unsigned int tag_hash_key_cal_hash_value(const struct tag_hash_key *my_hash_key)
-{
- return my_hash_key->hashv;
-}
-
-// used by HASH_FIND
-int tag_hash_key_cmp(const struct tag_hash_key *a, const struct tag_hash_key *b)
-{
- if (a->n_my_tag == 0 && b->n_my_tag == 0) {
- return 0;
- }
-
- if (a->n_my_tag != b->n_my_tag) {
- return 1;
- }
- if (a->hashv128.high64 != b->hashv128.high64 || a->hashv128.low64 != b->hashv128.low64) {
- return 1;
- }
-
- size_t tmp_tag_n = a->n_my_tag;
- const struct fieldstat_tag *tag_a = a->tags;
- const struct fieldstat_tag *tag_b = b->tags;
- for (size_t i = 0; i < tmp_tag_n; i++) {
- if (tag_a->type == TAG_CSTRING) {
- if (strcmp(tag_a->value_str, tag_b->value_str) != 0) {
- return 1;
- }
- } else {
- if (tag_a->value_longlong != tag_b->value_longlong) { // sizeof(long long) == sizeof(double)
- return 1;
- }
- }
-
- // its hardly possible that the keys are different while the value is the same. What's more, the tags are in the same 128 hash. So it's not necessary to compare the key.
- // if (strcmp(tag_a->key, tag_b->key) != 0) {
- // return 1;
- // }
-
- tag_a++;
- tag_b++;
- }
- return 0;
-}
-
-/* -------------------------------------------------------------------------- */
-/* fieldtag */
-/* -------------------------------------------------------------------------- */
-
-void fieldtag_copy(struct fieldstat_tag *dest, const struct fieldstat_tag *src)
-{
- dest->key = strdup(src->key);
- dest->type = src->type;
- switch(src->type)
- {
- case TAG_INTEGER:
- dest->value_longlong = src->value_longlong;
- break;
- case TAG_DOUBLE:
- dest->value_double = src->value_double;
- break;
- case TAG_CSTRING:
- dest->value_str = strdup(src->value_str);
- break;
- default:
- assert(0);
- break;
- }
-}
-
-void fieldtag_list_free(struct fieldstat_tag *tags, size_t n_tags)
-{
- if (tags == NULL) {
- return;
- }
- for (int i = 0; i < (int)n_tags; i++) {
- struct fieldstat_tag *tag = &tags[i];
- free((char *)tag->key);
- if (tag->type == TAG_CSTRING) {
- free((char *)tag->value_str);
- }
- }
- free(tags);
-}
-
-int my_cmp_tag(const void *a, const void *b)
-{
- const struct fieldstat_tag *aa = (struct fieldstat_tag *)a;
- const struct fieldstat_tag *bb = (struct fieldstat_tag *)b;
- int key_cmp = strcmp(aa->key, bb->key);
- if (key_cmp != 0) {
- return key_cmp;
- }
- if (aa->type != bb->type) {
- return aa->type - bb->type;
- }
-
- switch (aa->type) {
- case TAG_INTEGER:
- return aa->value_longlong - bb->value_longlong;
- case TAG_DOUBLE:
- return aa->value_double - bb->value_double;
- case TAG_CSTRING:
- return strcmp(aa->value_str, bb->value_str);
- default:
- return 0;
- }
-}
-
-unsigned int cal_tag_hash_xxhash(const struct fieldstat_tag *tag, size_t n_tag, unsigned int seed)
-{
- XXH32_state_t state;
- XXH32_reset(&state, seed);
-
- for (int i = 0; i < n_tag; i++)
- {
- XXH32_update(&state, (const xxh_u8 *)tag[i].key, strlen(tag[i].key));
-
- if (tag[i].type != TAG_CSTRING) { // sizeof(long long) == sizeof(double)
- XXH32_update(&state, (const xxh_u8 *)&tag[i].value_longlong, sizeof(long long));
- } else {
- XXH32_update(&state, (const xxh_u8 *)tag[i].value_str, strlen(tag[i].value_str));
- }
- }
-
- return XXH32_digest(&state);
-}
-
-XXH128_hash_t cal_tag_hash_xxhash128_obsolete(const struct fieldstat_tag *tag, size_t n_tag, unsigned int seed)
-{
- XXH3_state_t state = {0};
- XXH3_128bits_reset_withSeed(&state, seed);
-
- for (int i = 0; i < n_tag; i++)
- {
- XXH3_128bits_update(&state, tag[i].key, strlen(tag[i].key));
-
- if (tag[i].type != TAG_CSTRING) { // sizeof(long long) == sizeof(double)
- XXH3_128bits_update(&state, &tag[i].value_longlong, sizeof(long long));
- } else {
- XXH3_128bits_update(&state, tag[i].value_str, strlen(tag[i].value_str));
- }
- }
-
- XXH128_hash_t ret = XXH3_128bits_digest(&state);
-
- return ret;
-}
-
-#define BIG_CONSTANT(x) (x##LLU)
-#define getblock(p, i) (p[i])
-#ifdef __GNUC__
-#define FORCE_INLINE __attribute__((always_inline)) inline
-#else
-#define FORCE_INLINE inline
-#endif
-static FORCE_INLINE uint64_t rotl64 ( uint64_t x, int8_t r )
-{
- return (x << r) | (x >> (64 - r));
-}
-#define ROTL64(x,y) rotl64(x,y)
-#define C1 0x87c37b91114253d5LLU
-#define C2 0x4cf5ad432745937fLLU
-
-static FORCE_INLINE uint64_t fmix64 ( uint64_t k )
-{
- k ^= k >> 33;
- k *= BIG_CONSTANT(0xff51afd7ed558ccd);
- k ^= k >> 33;
- k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
- k ^= k >> 33;
-
- return k;
-}
-
-static FORCE_INLINE void murmurhash_string(uint64_t *h1_io, uint64_t *h2_io, const char *key, const int len) {
- uint64_t h1 = *h1_io;
- uint64_t h2 = *h2_io;
- const uint8_t * data = (const uint8_t*)key;
- const int nblocks = len / 16;
- int i;
-
- // body
- const uint64_t * blocks = (const uint64_t *)(data);
-
- for(i = 0; i < nblocks; i++)
- {
- uint64_t k1 = getblock(blocks,i*2+0);
- uint64_t k2 = getblock(blocks,i*2+1);
-
- k1 *= C1; k1 = ROTL64(k1,31); k1 *= C2; h1 ^= k1;
-
- h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;
-
- k2 *= C2; k2 = ROTL64(k2,33); k2 *= C1; h2 ^= k2;
-
- h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
- }
-
- //----------
- // tail
-
- const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
-
- uint64_t k1 = 0;
- uint64_t k2 = 0;
-
- switch(len & 15)
- {
- case 15: k2 ^= (uint64_t)(tail[14]) << 48;
- case 14: k2 ^= (uint64_t)(tail[13]) << 40;
- case 13: k2 ^= (uint64_t)(tail[12]) << 32;
- case 12: k2 ^= (uint64_t)(tail[11]) << 24;
- case 11: k2 ^= (uint64_t)(tail[10]) << 16;
- case 10: k2 ^= (uint64_t)(tail[ 9]) << 8;
- case 9: k2 ^= (uint64_t)(tail[ 8]) << 0;
- k2 *= C2; k2 = ROTL64(k2,33); k2 *= C1; h2 ^= k2;
-
- case 8: k1 ^= (uint64_t)(tail[ 7]) << 56;
- case 7: k1 ^= (uint64_t)(tail[ 6]) << 48;
- case 6: k1 ^= (uint64_t)(tail[ 5]) << 40;
- case 5: k1 ^= (uint64_t)(tail[ 4]) << 32;
- case 4: k1 ^= (uint64_t)(tail[ 3]) << 24;
- case 3: k1 ^= (uint64_t)(tail[ 2]) << 16;
- case 2: k1 ^= (uint64_t)(tail[ 1]) << 8;
- case 1: k1 ^= (uint64_t)(tail[ 0]) << 0;
- k1 *= C1; k1 = ROTL64(k1,31); k1 *= C2; h1 ^= k1;
- };
-
- *h1_io = h1;
- *h2_io = h2;
-}
-
-static FORCE_INLINE void murmurhash_int64(uint64_t *h2_io, uint64_t h1, long long value) {
- // uint64_t h1 = *h1_io;
- uint64_t h2 = *h2_io;
- uint64_t k2 = value;
-
- k2 *= C2; k2 = ROTL64(k2,33); k2 *= C1; h2 ^= k2;
- h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
-
- // *h1_io = h1;
- *h2_io = h2;
-}
-
-XXH128_hash_t my_murmurHash_128_tag(const struct fieldstat_tag *tag, size_t n_tag, unsigned int seed)
-{
- uint64_t h1 = seed;
- uint64_t h2 = seed;
- const struct fieldstat_tag *tmp_tag;
-
- for (int i = 0; i < n_tag; i++) {
- tmp_tag = tag + i;
-
- murmurhash_string(&h1, &h2, tmp_tag->key, strlen(tmp_tag->key));
- if (tmp_tag->type != TAG_CSTRING) {
- murmurhash_int64(&h2, h1, tmp_tag->value_longlong); // sizeof(long long) == sizeof(double)
- } else {
- murmurhash_string(&h1, &h2, tmp_tag->value_str, strlen(tmp_tag->value_str));
- }
- }
-
- // finalization
- h1 += h2;
- h2 += h1;
-
- h1 = fmix64(h1);
- h2 = fmix64(h2);
-
- h1 += h2;
- h2 += h1;
-
- XXH128_hash_t ret;
- ret.low64 = h1;
- ret.high64 = h2;
- return ret;
-}
-
-XXH128_hash_t cal_tag_hash_xxhash128(const struct fieldstat_tag *tag, size_t n_tag, unsigned int seed) {
- return my_murmurHash_128_tag(tag, n_tag, seed);
-}
-
-void tag_hash_key_init_with_fieldstat_tag(struct tag_hash_key *tag_key, const struct fieldstat_tag *src, size_t n_src, bool deep_copy)
-{
- tag_key->n_my_tag = n_src;
- tag_key->is_deep_copy = deep_copy;
- if (n_src == 0) {
- tag_key->tags = NULL;
- tag_key->hashv = 0;
- return;
- }
-
- tag_key->hashv128 = cal_tag_hash_xxhash128(src, n_src, 0);
- tag_key->hashv = tag_key->hashv128.high64;
-
- if (deep_copy) {
- tag_key->tags = (struct fieldstat_tag *)malloc(sizeof(struct fieldstat_tag) * n_src);
- for (int i = 0; i < (int)n_src; i++) {
- fieldtag_copy(&tag_key->tags[i], &src[i]);
- }
- } else {
- tag_key->tags = (struct fieldstat_tag *)src;
- }
-}
-
-void tag_hash_key_convert_to_fieldstat_tag(const struct tag_hash_key *tag_key, struct fieldstat_tag **out, size_t *n_out)
-{
- size_t ret_n_tag = tag_key->n_my_tag;
- *n_out = ret_n_tag;
- if (ret_n_tag == 0) {
- *out = NULL;
- return;
- }
-
- struct fieldstat_tag *ret_fieldstat_tag = (struct fieldstat_tag *)malloc(sizeof(struct fieldstat_tag) * ret_n_tag);
- *out = ret_fieldstat_tag;
-
- for (int i = 0; i < ret_n_tag; i++) {
- fieldtag_copy(&ret_fieldstat_tag[i], &tag_key->tags[i]);
- }
-}
-
-struct tag_hash_key *tag_hash_key_copy(const struct tag_hash_key *src)
-{
- struct tag_hash_key *dst = (struct tag_hash_key *)malloc(sizeof(struct tag_hash_key));
- dst->n_my_tag = src->n_my_tag;
- dst->hashv = src->hashv;
- dst->is_deep_copy = true;
- dst->hashv128 = src->hashv128;
-
- if (dst->n_my_tag == 0) {
- dst->tags = NULL;
- } else {
- dst->tags = (struct fieldstat_tag *)malloc(sizeof(struct fieldstat_tag) * dst->n_my_tag);
- for (int i = 0; i < dst->n_my_tag; i++) {
- fieldtag_copy(&dst->tags[i], &src->tags[i]);
- }
- }
-
- return dst;
-}
-
-void tag_hash_key_free(struct tag_hash_key *tag_key)
-{
- if (tag_key->is_deep_copy) {
- fieldtag_list_free(tag_key->tags, tag_key->n_my_tag);
- }
- free(tag_key);
-}
-
-unsigned tag_hash_key_cal_hash_val(const struct tag_hash_key *tag, unsigned seed) {
- if (seed == 0) { // use default seed
- return tag->hashv;
- }
-
- // https://www.eecs.harvard.edu/~michaelm/postscripts/tr-02-05.pdf
- // A technique from the hashing literature is to use two hash functions h1(x) and h2(x) to simulate additional hash functions of the form gi(x) = h1(x) + ih2(x)
- // Assuming that the 128-bit xxhash function is perfect, we can view it as a combination of two 64-bit hash functions.
- unsigned h1 = (unsigned)tag->hashv128.high64;
- unsigned h2 = (unsigned)tag->hashv128.low64;
- unsigned ret = h1 + seed * h2;
-
- return ret;
-}
-
-void build_dynamic_cell_key(const struct fieldstat_tag tags[], size_t n_tags, char **out_key, size_t *out_key_size)
-{
- if (n_tags == 0) {
- // use a default dummy key
- *out_key = strdup("no tags");
- *out_key_size = strlen(*out_key);
- return;
- }
- int i = 0;
- int used_len = 0;
- struct fieldstat_tag *tag = NULL;
-
- const size_t init_key_size = 1024;
- size_t one_time_key_size = init_key_size;
- char *one_time_key = (char *)malloc(init_key_size);
- size_t remain_key_size = init_key_size;
- size_t total_key_size = init_key_size;
- char *dynamic_mem = (char *)malloc(remain_key_size);
- size_t key_len;
- size_t val_len;
-
- for(i = 0; i < (int)n_tags; i++)
- {
- tag = (struct fieldstat_tag *)&tags[i];
- switch(tag->type)
- {
- case 0:
- key_len = strlen(tag->key);
- val_len = sizeof(long long);
- used_len = key_len + val_len;
- while (used_len >= one_time_key_size) {
- one_time_key_size += init_key_size;
- one_time_key = (char *)realloc(one_time_key, one_time_key_size);
- }
- memcpy(one_time_key, tag->key, key_len);
- memcpy(one_time_key + key_len, &tag->value_longlong, val_len);
- break;
- case 1:
- key_len = strlen(tag->key);
- val_len = sizeof(double);
- used_len = key_len + val_len;
- while (used_len >= one_time_key_size) {
- one_time_key_size += init_key_size;
- one_time_key = (char *)realloc(one_time_key, one_time_key_size);
- }
- memcpy(one_time_key, tag->key, key_len);
- memcpy(one_time_key + key_len, &tag->value_double, val_len);
- break;
- case 2:
- key_len = strlen(tag->key);
- val_len = strlen(tag->value_str);
- used_len = key_len + val_len;
- while (used_len >= one_time_key_size) {
- one_time_key_size += init_key_size;
- one_time_key = (char *)realloc(one_time_key, one_time_key_size);
- }
- memcpy(one_time_key, tag->key, key_len);
- memcpy(one_time_key + key_len, tag->value_str, val_len);
- break;
- default:
- assert(0);
- break;
- }
-
- while (used_len >= remain_key_size) {
- total_key_size += init_key_size;
- dynamic_mem = (char *)realloc(dynamic_mem, total_key_size);
- remain_key_size += init_key_size;
- }
-
- memcpy(dynamic_mem + total_key_size - remain_key_size, one_time_key, used_len);
- remain_key_size -= used_len;
- }
-
- *out_key = (char *)malloc(total_key_size - remain_key_size + 1);
- memcpy(*out_key, dynamic_mem, total_key_size - remain_key_size);
- (*out_key)[total_key_size - remain_key_size] = '\0';
- *out_key_size = total_key_size - remain_key_size; // not include '\0'
-
- free(dynamic_mem);
- free(one_time_key);
-}
-
-char *tag_hash_key_get_compound_key(const struct tag_hash_key *tag)
-{
- char *key;
- size_t key_size;
- build_dynamic_cell_key(tag->tags, tag->n_my_tag, &key, &key_size);
- return key;
-} \ No newline at end of file
diff --git a/src/tags/my_ut_hash.h b/src/tags/my_ut_hash.h
deleted file mode 100644
index 1066569..0000000
--- a/src/tags/my_ut_hash.h
+++ /dev/null
@@ -1,32 +0,0 @@
-
-#pragma once
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-#include "fieldstat.h"
-#include "xxhash/xxhash.h"
-
-struct tag_hash_key {
- struct fieldstat_tag *tags; // not used for hash, just to recover the original tags.
- size_t n_my_tag;
- unsigned int hashv;
- XXH128_hash_t hashv128;
- bool is_deep_copy;
-};
-
-void tag_hash_key_init_with_fieldstat_tag(struct tag_hash_key *tag_key, const struct fieldstat_tag *src, size_t n_src, bool deep_copy);
-void tag_hash_key_convert_to_fieldstat_tag(const struct tag_hash_key *tag_key, struct fieldstat_tag **out, size_t *n_out);
-struct tag_hash_key *tag_hash_key_copy(const struct tag_hash_key *src); // Always deep copy
-void tag_hash_key_free(struct tag_hash_key *tag_key);
-unsigned tag_hash_key_cal_hash_val(const struct tag_hash_key *tag, unsigned seed);
-
-void fieldtag_list_free(struct fieldstat_tag *tags, size_t n_tags);
-char *tag_hash_key_get_compound_key(const struct tag_hash_key *tag);
-int tag_hash_key_cmp(const struct tag_hash_key *a, const struct tag_hash_key *b);
-
-#ifdef __cplusplus
-}
-#endif \ No newline at end of file
diff --git a/src/tags/my_ut_hash_inner.h b/src/tags/my_ut_hash_inner.h
deleted file mode 100644
index 628b471..0000000
--- a/src/tags/my_ut_hash_inner.h
+++ /dev/null
@@ -1,38 +0,0 @@
-
-#pragma once
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-#include <stdbool.h>
-#include <stddef.h>
-
-struct tag_hash_key;
-
-unsigned int tag_hash_key_cal_hash_value(const struct tag_hash_key *my_hash_key);
-// return 1 if not equal, 0 if equal
-int tag_hash_key_cmp(const struct tag_hash_key *a, const struct tag_hash_key *b);
-
-// cover the original HASH_FUNCTION
-#define HASH_FUNCTION(keyptr, keylen, hashv) \
- do { \
- hashv = tag_hash_key_cal_hash_value((struct tag_hash_key *)keyptr); \
- } while (0)
-
-#define HASH_KEYCMP(a, b, n) (tag_hash_key_cmp((const struct tag_hash_key *)a, (const struct tag_hash_key *)b))
-
-#include "uthash.h"
-
-#define HASH_ADD_TAG(head,fieldname,add) \
- HASH_ADD_KEYPTR(hh, head, (add)->fieldname, 1, add)
-
-#define HASH_FIND_TAG(head,keyptr,out) \
- HASH_FIND(hh, head, keyptr, 1, out)
-
-
-
-#ifdef __cplusplus
-}
-#endif \ No newline at end of file
diff --git a/src/tags/sorted_set.c b/src/tags/sorted_set.c
deleted file mode 100644
index ae315d5..0000000
--- a/src/tags/sorted_set.c
+++ /dev/null
@@ -1,464 +0,0 @@
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <assert.h>
-
-#include "my_ut_hash_inner.h"
-#include "xxhash/xxhash.h"
-#include "minheap/heap.h"
-
-#include "sorted_set.h"
-#include "my_ut_hash.h"
-
-
-struct entry_data {
- struct tag_hash_key *key;
-
- int cell_id;
-};
-
-struct sorted_set {
- heap *heap;
- struct hash_node *hash_tbl;
-
- int n_living_entry;
-};
-
-struct hash_node {
- struct tag_hash_key *key; // value is equal to entry_data::key(point to the same memory)
- heap_entry *val;
- UT_hash_handle hh;
-};
-
-struct entry_data *entry_data_construct(const struct tag_hash_key *tag, int cell_id)
-{
- assert(cell_id >= 0);
- assert(tag != NULL);
- long long tmp_v = tag->hashv;
- assert(tmp_v >= 0);
-
- struct entry_data *entry_data = (struct entry_data *)malloc(sizeof(struct entry_data));
-
- entry_data->key = tag_hash_key_copy(tag);
- entry_data->cell_id = cell_id;
- return entry_data;
-}
-
-void entry_data_destroy(struct entry_data *entry_data)
-{
- if (entry_data == NULL) {
- return;
- }
- tag_hash_key_free(entry_data->key);
- free(entry_data);
-}
-
-unsigned long long sorted_set_entry_get_count(const heap_entry *entry)
-{
- unsigned long long count = *(unsigned long long *)entry->key;
- if (count == 0) {
- return 0;
- }
- return count - 1; // sorted set will let the count start from 1, 0 for dying entry.
-}
-
-struct entry_data *sorted_set_entry_get_data(const heap_entry *entry)
-{
- assert(entry != NULL);
- assert(entry->value != NULL);
-
- const struct entry_data * tmp_val = (struct entry_data *)entry->value;
- assert(tmp_val->key != NULL);
- long long tmp_v = tmp_val->key->hashv;
- assert(tmp_v >= 0);
-
- assert(tmp_val->cell_id >= 0);
- return entry->value;
-}
-
-/*
- dying: To reduce the time of HASH_ADD, and to support merely recording a key without any value, give a dying status.
-*/
-bool sorted_set_entry_dying(const heap_entry *entry)
-{
- unsigned long long count = *(unsigned long long *)entry->key;
- return count == 0;
-}
-
-// cppcheck-suppress [constParameterCallback, unmatchedSuppression]
-static bool cmp_int(void *aa, void *bb)
-{
- heap_entry *a = (heap_entry *)aa;
- heap_entry *b = (heap_entry *)bb;
- unsigned long long count_a = *(unsigned long long *)a->key;
- unsigned long long count_b = *(unsigned long long *)b->key;
-
- if (count_a < count_b) {
- return true;
- }
- return false;
-}
-
-static int my_entry_get_index(void *a)
-{
- return ((heap_entry *)a)->index;
-}
-
-void my_entry_set_index(int index, void *a)
-{
- ((heap_entry *)a)->index = index;
-}
-
-struct sorted_set *sorted_set_new(int set_size)
-{
- struct sorted_set *ret = (struct sorted_set *)malloc(sizeof(struct sorted_set));
- ret->hash_tbl = NULL;
- ret->n_living_entry = 0;
- heap *h = (heap *)malloc(sizeof(heap));
- init_heap(h, set_size, cmp_int, my_entry_get_index, my_entry_set_index);
-
- ret->heap = h;
- return ret;
-}
-
-void sorted_set_free(struct sorted_set *ss)
-{
- struct hash_node *tbl = ss->hash_tbl;
- struct hash_node *h_node, *tmp;
- HASH_ITER(hh, tbl, h_node, tmp) {
- HASH_DEL(tbl, h_node);
- free(h_node);
- }
-
- heap *heap = ss->heap;
- heap_entry *node;
- for (int i = 0; i < heap->cur_size; i++) {
- node = (heap_entry *)heap->nodes[i];
- entry_data_destroy(node->value);
- free(node->key);
- free(node);
- }
- free(heap->nodes);
- free(heap);
-
- free(ss);
-}
-
-heap_entry *sorted_set_find_entry(const struct sorted_set *ss, const struct tag_hash_key *tag)
-{
- struct hash_node *hash_tbl = ss->hash_tbl;
- struct hash_node *s = NULL;
- HASH_FIND_TAG(hash_tbl, tag, s);
-
- if (s == NULL) {
- return NULL;
- }
- return (heap_entry *)s->val;
-}
-
-void sorted_set_entry_set_key(struct sorted_set *ss, heap_entry *entry, unsigned long long count)
-{
- *(unsigned long long *)(entry->key) = count;
-
- adjust_heap_node(ss->heap, entry);
-}
-
-unsigned long long sorted_set_pop_and_record_popped_back(struct sorted_set *ss, int *popped_cell_id)
-{
- struct hash_node *hash_tbl = ss->hash_tbl;
- heap_entry *entry = (heap_entry *)pop_heap(ss->heap);
- if (entry == NULL) {
- return 0;
- }
- unsigned long long count_ret = sorted_set_entry_get_count(entry);
- struct entry_data *tmp_data = sorted_set_entry_get_data(entry);
-
- struct hash_node *s = NULL;
- HASH_FIND_TAG(hash_tbl, tmp_data->key, s);
- if (s == NULL) {
- return 0;
- }
-
- HASH_DEL(hash_tbl, s);
- free(s);
- ss->hash_tbl = hash_tbl;
-
- if (sorted_set_entry_dying(entry)) {
- *popped_cell_id = -1;
- } else {
- *popped_cell_id = tmp_data->cell_id;
- ss->n_living_entry--;
- }
-
- entry_data_destroy(tmp_data);
- free(entry->key);
- free(entry);
-
- return count_ret;
-}
-
-unsigned long long sorted_set_pop(struct sorted_set *ss)
-{
- int dummy;
- unsigned long long count_ret = sorted_set_pop_and_record_popped_back(ss, &dummy);
-
- return count_ret;
-}
-
-bool sorted_set_check_is_full(const struct sorted_set *ss)
-{
- return ss->heap->cur_size >= ss->heap->max_size;
-}
-
-unsigned long long safe_add(unsigned long long a, unsigned long long b)
-{
- if (UINT64_MAX - a < b) {
- return UINT64_MAX;
- }
- return a + b;
-}
-
-void sorted_set_insert_to_available_heap(struct sorted_set *ss, const struct tag_hash_key *tag, unsigned long long cnt, int cell_id)
-{
- cnt = safe_add(cnt, 1); // sorted set will let the count start from 1, 0 for dying entry.
- unsigned long long *tmp_cnt = (unsigned long long*)malloc(sizeof(unsigned long long));
- *tmp_cnt = cnt;
- struct entry_data *tmp_data = entry_data_construct(tag, cell_id);
- heap_entry *node = (heap_entry *)malloc(sizeof(heap_entry));
- node->key = tmp_cnt;
- node->value = tmp_data;
-
- push_heap(ss->heap, (void *)node);
-
- struct hash_node *hash_tbl = ss->hash_tbl;
- struct hash_node *s = (struct hash_node *)malloc(sizeof(struct hash_node));
- s->key = sorted_set_entry_get_data(node)->key;
- s->val = node;
- HASH_ADD_TAG(hash_tbl, key, s);
- ss->hash_tbl = hash_tbl;
-
- ss->n_living_entry++;
-}
-
-int sorted_set_insert(struct sorted_set *ss, const struct tag_hash_key *tag, unsigned long long cnt, int cell_id)
-{
- assert(cell_id >= 0);
- assert(tag != NULL);
- long long tmp_v = tag->hashv;
- assert(tmp_v >= 0);
-
- if (sorted_set_check_is_full(ss)) {
- unsigned long long tmp_mincnt = sorted_set_get_min_count(ss);
- if (tmp_mincnt != INVALID_COUNT && cnt <= tmp_mincnt) { // even if all cells in sorted set are dying, the sorted set can still be a full one, in which case, the min count is invalid.
- return 0;
- }
- sorted_set_pop(ss);
- }
- sorted_set_insert_to_available_heap(ss, tag, cnt, cell_id);
- return 1;
-}
-
-int sorted_set_insert_and_record_popped_back(struct sorted_set *ss, const struct tag_hash_key *tag, unsigned long long cnt, int cell_id, int *popped_user_data)
-{
- assert(cell_id >= 0);
- assert(tag != NULL);
- long long tmp_v = tag->hashv;
- assert(tmp_v >= 0);
-
- // if there is a dying record, reborn it to use.
- heap_entry *entry = sorted_set_find_entry(ss, tag);
- if (entry != NULL) {
- if (!sorted_set_entry_dying(entry)) {
- return -1;
- }
- sorted_set_entry_set_key(ss, entry, safe_add(cnt, 1)); // sorted set will let the count start from 1, 0 for dying entry.
- struct entry_data *data = sorted_set_entry_get_data(entry);
- data->cell_id = cell_id;
- ss->n_living_entry++;
- return 3;
- }
-
- bool popped = false;
- if (sorted_set_check_is_full(ss)) {
- unsigned long long tmp_mincnt = sorted_set_get_min_count(ss);
- if (tmp_mincnt != INVALID_COUNT && cnt <= tmp_mincnt) { // even if all cells in sorted set are dying, the sorted set can still be a full one, in which case, the min count is invalid.
- return 0;
- }
- sorted_set_pop_and_record_popped_back(ss, popped_user_data);
- if (*popped_user_data == -1) { // popped a dying entry, not a real entry.
- popped = false;
- } else {
- popped = true;
- }
- }
- sorted_set_insert_to_available_heap(ss, tag, cnt, cell_id);
- return popped ? 2 : 1;
-}
-
-unsigned long long sorted_set_get_min_count(const struct sorted_set *ss)
-{
- heap *heap = ss->heap;
- if (heap->cur_size == 0) {
- return INVALID_COUNT;
- }
- const heap_entry *ret = (heap_entry *)(heap->nodes[0]);
-
- unsigned long long count = *(unsigned long long *)ret->key;
- if (count == 0) {
- return INVALID_COUNT;
- }
- return count - 1; // sorted set will let the count start from 1, 0 for dying entry.
-}
-
-unsigned long long sorted_set_get_count(const struct sorted_set *ss, const struct tag_hash_key *tag)
-{
- if (sorted_set_cardinality(ss) == 0) {
- return INVALID_COUNT;
- }
- const heap_entry *entry = sorted_set_find_entry(ss, tag);
- if (entry == NULL) {
- return INVALID_COUNT;
- }
- if (sorted_set_entry_dying(entry)) {
- return INVALID_COUNT;
- }
-
- return sorted_set_entry_get_count(entry);
-}
-
-int sorted_set_get_cell_id(const struct sorted_set *ss, const struct tag_hash_key *tag)
-{
- if (sorted_set_cardinality(ss) == 0) {
- return -1;
- }
- const heap_entry *entry = sorted_set_find_entry(ss, tag);
- if (entry == NULL) {
- return -1;
- }
- if (sorted_set_entry_dying(entry)) {
- return -1;
- }
-
- return sorted_set_entry_get_data(entry)->cell_id;
-}
-
-int sorted_set_cardinality(const struct sorted_set *ss)
-{
- return ss->n_living_entry;
-}
-
-int sorted_set_incrby(struct sorted_set *ss, const struct tag_hash_key *tag, unsigned long long count)
-{
- heap_entry *entry = sorted_set_find_entry(ss, tag);
- if (entry == NULL) {
- return -1;
- }
- unsigned long long cnt_old;
- if (sorted_set_entry_dying(entry) == false) {
- cnt_old = sorted_set_entry_get_count(entry);
- cnt_old += 1; // sorted set will let the count start from 1, 0 for dying entry.
- } else {
- cnt_old = 0;
- }
- sorted_set_entry_set_key(ss, entry, safe_add(count, cnt_old));
- return 0;
-}
-
-typedef struct {
- unsigned long long key;
- struct entry_data *val;
-} tmp_heap_node;
-
-int cmp_tmp_heap_node(const void *a, const void *b)
-{
- const tmp_heap_node *aa = (tmp_heap_node *)a;
- const tmp_heap_node *bb = (tmp_heap_node *)b;
-
- if (aa->key < bb->key) {
- return 1;
- } else if (aa->key > bb->key) {
- return -1;
- } else {
- return 0;
- }
-}
-
-void sorted_set_dump(const struct sorted_set *ss, unsigned long long *scores_out, struct tag_hash_key **tags_out, int *user_data_out)
-{
- struct minheap *h = ss->heap;
- tmp_heap_node *tmp_nodes = (tmp_heap_node *)malloc(sizeof(tmp_heap_node) * h->cur_size);
- size_t n_living_entry = 0;
- for (int i = 0; i < h->cur_size; i++) {
- const heap_entry *entry = (heap_entry *)h->nodes[i];
- if (sorted_set_entry_dying(entry)) {
- continue;
- }
- tmp_nodes[n_living_entry].key = sorted_set_entry_get_count(entry);
- tmp_nodes[n_living_entry].val = sorted_set_entry_get_data(entry);
- n_living_entry++;
- }
- assert(n_living_entry == ss->n_living_entry);
- qsort(tmp_nodes, n_living_entry, sizeof(tmp_heap_node), cmp_tmp_heap_node);
- for (int i = 0; i < n_living_entry; i++) {
- assert(tmp_nodes[i].val != NULL);
- assert(tmp_nodes[i].val->cell_id >= 0);
- assert(tmp_nodes[i].val->key != NULL);
- long long tmp_haha = tmp_nodes[i].val->key->hashv;
- assert(tmp_haha >= 0);
-
- scores_out[i] = tmp_nodes[i].key;
- tags_out[i] = tmp_nodes[i].val->key;
- user_data_out[i] = tmp_nodes[i].val->cell_id;
- }
- free(tmp_nodes);
-}
-
-struct sorted_set *sorted_set_copy(const struct sorted_set *ss)
-{
- struct sorted_set *ret = sorted_set_new(ss->heap->max_size);
-
- for (int i = 0; i < ss->heap->cur_size; i++) {
- const heap_entry *entry = (heap_entry *)ss->heap->nodes[i];
- if (sorted_set_entry_dying(entry)) {
- continue;
- }
- const struct entry_data *data = sorted_set_entry_get_data(entry);
- sorted_set_insert(ret, data->key, sorted_set_entry_get_count(entry), data->cell_id);
- }
-
- return ret;
-}
-
-// set all the entry to dying status. They will be the first to be popped.
-void sorted_set_reset(struct sorted_set *ss)
-{
- struct hash_node *tbl = ss->hash_tbl;
- struct hash_node *h_node, *tmp;
- HASH_ITER(hh, tbl, h_node, tmp) {
- heap_entry *entry = (heap_entry *)h_node->val;
- *(unsigned long long *)entry->key = 0;
- }
-
- heap *heap = ss->heap;
- heap_entry *entry;
- for (int i = 0; i < heap->cur_size; i++) {
- entry = (heap_entry *)heap->nodes[i];
- *(unsigned long long *)entry->key = 0;
- }
-
- ss->n_living_entry = 0;
-}
-
-struct tag_hash_key *sorted_set_get_true_pst_tag(const struct sorted_set *ss, const struct tag_hash_key *tag)
-{
- const heap_entry *entry = sorted_set_find_entry(ss, tag);
- if (entry == NULL) {
- return NULL;
- }
- if (sorted_set_entry_dying(entry)) {
- return NULL;
- }
- const struct entry_data *data = sorted_set_entry_get_data(entry);
- return data->key;
-} \ No newline at end of file
diff --git a/src/tags/sorted_set.h b/src/tags/sorted_set.h
deleted file mode 100644
index 6dee8c7..0000000
--- a/src/tags/sorted_set.h
+++ /dev/null
@@ -1,89 +0,0 @@
-#pragma once
-
-#include <stddef.h>
-#include "my_ut_hash.h"
-
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-#define INVALID_COUNT UINT64_MAX
-
-struct sorted_set;
-
-/**
- * @brief Create a new sorted set.
- * @param set_size the max size of the sorted set.
- * @return the pointer to the sorted set.
-*/
-struct sorted_set *sorted_set_new(int set_size);
-
-/**
- * @brief Destroy the sorted set.
- * @param ss the pointer to the sorted set
-*/
-void sorted_set_free(struct sorted_set *ss);
-
-
-/**
- * @brief insert a new entry into the sorted set. The minimum entry will be popped if the list is full.
- * @param tag the pointer to the tag, user should alloc it, when the entry is popped out, the tag will be freed. If the tag is not inserted to the sorted set, user should free it.
- * @return 0 if the entry is not inserted because the sorted set is full and the score of the entry is less than the minimum score in the sorted set. Otherwise, return 1.
-*/
-int sorted_set_insert(struct sorted_set *ss, const struct tag_hash_key *tag, unsigned long long cnt, int cell_id);
-
-/*
- @return -1 if the entry already exist. 0 if the entry is not inserted because the sorted set is full. return 2 if some data is popped out. Otherwise, return 1 if the entry is inserted successfully and sorted set not full.
- @param popped_user_data user should alloc it, popped_user_data_size is equal to the size of the user data memory.
-*/
-int sorted_set_insert_and_record_popped_back(struct sorted_set *ss, const struct tag_hash_key *tag, unsigned long long cnt, int cell_id, int *popped_user_data);
-
-/**
- * @brief pop out the minumum-score entry from the sorted set.
- * @param ss the sorted set
- * @return the score of the minimum-score member. return 0 if the sorted set is empty.
-*/
-unsigned long long sorted_set_pop(struct sorted_set *ss);
-
-/**
- * @brief Increase the score of the given member.
- * @param score By the value of which increase member.
-*/
-int sorted_set_incrby(struct sorted_set *ss, const struct tag_hash_key *tag, unsigned long long count);
-
-/**
- * @brief get the minimum score from the sorted set.
- * @param ss the sorted set
- * @return the score of the minimum-score member
-*/
-unsigned long long sorted_set_get_min_count(const struct sorted_set *ss);
-
-/**
- * @brief find the how many elements the sorted set contains.
-*/
-int sorted_set_cardinality(const struct sorted_set *ss);
-
-/**
- * @brief Get the score of the given key string.
- * @return the score of the member. return 0 if the member is not in the sorted set.
-*/
-unsigned long long sorted_set_get_count(const struct sorted_set *ss, const struct tag_hash_key *tag);
-int sorted_set_get_cell_id(const struct sorted_set *ss, const struct tag_hash_key *tag);
-/**
- * @brief Get all the contents in sorted order from small to big.
- * @param scores_out the scores of the entries. User should alloc it and make sure the buffer is long enough.
- * @param tags_out the tags of the entries. User should alloc it and make sure the buffer is long enough. Don't free the content.
- * @param user_data_out the user data of the entries. User should alloc it and make sure the buffer is long enough. Its content is the pointer to the user data in the sorted set. Don't free the content.
-*/
-void sorted_set_dump(const struct sorted_set *ss, unsigned long long *scores_out, struct tag_hash_key **tags_out, int *user_data_out);
-
-struct sorted_set *sorted_set_copy(const struct sorted_set *ss);
-
-void sorted_set_reset(struct sorted_set *ss);
-
-// the cell_manager id_tag_map will not stort the copied tag, instead, it will store the pointer to the tag in the sorted set. Use this function to give it one.
-struct tag_hash_key *sorted_set_get_true_pst_tag(const struct sorted_set *ss, const struct tag_hash_key *tag);
-
-#ifdef __cplusplus
-}//end extern "C"
-#endif