// Start of event_list.h typedef int (*event_report_fn)(struct str_builder*, void*); // A collection of key-value associations. Used to associate extra data with // events. struct kvs { // A buffer that contains all value data. Must be freed when the struct kvs is // no longer used. char *buf; // Size of buf in bytes. size_t buf_size; // Number of bytes used in buf. size_t buf_used; // Number of associations stored. size_t n; // Capacity of vals. size_t vals_capacity; // An array of keys. const char* *keys; // Indexes into 'buf' that contains the values as zero-terminated strings. size_t *vals; }; static const size_t KVS_INIT_BUF_SIZE = 128; static const size_t KVS_INIT_NUMKEYS = 8; void kvs_init(struct kvs* kvs) { kvs->buf = malloc(KVS_INIT_BUF_SIZE); kvs->buf_size = KVS_INIT_BUF_SIZE; kvs->buf_used = 0; kvs->vals_capacity = KVS_INIT_NUMKEYS; kvs->keys = calloc(kvs->vals_capacity, sizeof(const char*)); kvs->vals = calloc(kvs->vals_capacity, sizeof(size_t)); kvs->n = 0; } struct kvs* kvs_new(void) { struct kvs *kvs = malloc(sizeof(struct kvs)); kvs_init(kvs); return kvs; } void kvs_printf(struct kvs* kvs, const char* key, const char* fmt, ...) { va_list vl; va_start(vl, fmt); size_t needed = 1 + (size_t)vsnprintf(NULL, 0, fmt, vl); while (kvs->buf_used+needed > kvs->buf_size) { kvs->buf_size *= 2; kvs->buf = realloc(kvs->buf, kvs->buf_size * sizeof(const char*)); } if (kvs->n == kvs->vals_capacity) { kvs->vals_capacity *= 2; kvs->vals = realloc(kvs->vals, kvs->vals_capacity * sizeof(size_t)); kvs->keys = realloc(kvs->keys, kvs->vals_capacity * sizeof(char*)); } kvs->keys[kvs->n] = key; kvs->vals[kvs->n] = kvs->buf_used; kvs->buf_used += needed; va_start(vl, fmt); // Must re-init. vsnprintf(&kvs->buf[kvs->vals[kvs->n]], needed, fmt, vl); kvs->n++; } void kvs_free(struct kvs* kvs) { free(kvs->vals); free(kvs->keys); free(kvs->buf); } // Assumes all of the values are valid JSON objects. void kvs_json(const struct kvs* kvs, struct str_builder *sb) { str_builder_char(sb, '{'); for (size_t i = 0; i < kvs->n; i++) { if (i != 0) { str_builder_str(sb, ","); } str_builder_json_str(sb, kvs->keys[i]); str_builder_str(sb, ":"); str_builder_str(sb, &kvs->buf[kvs->vals[i]]); } str_builder_char(sb, '}'); } void kvs_log(const struct kvs* kvs, const char* prefix, FILE* f) { for (size_t i = 0; i < kvs->n; i++) { fprintf(f, "%s%s: %s\n", prefix, kvs->keys[i], &kvs->buf[kvs->vals[i]]); } } struct event { void* data; event_report_fn f; const char* name; const char *provenance; // Key-value information that is also to be printed. struct kvs *kvs; }; struct event_list { struct event *events; int num_events; int capacity; }; static void event_list_init(struct event_list *l) { l->capacity = 100; l->num_events = 0; l->events = calloc(l->capacity, sizeof(struct event)); } static void event_list_free(struct event_list *l) { free(l->events); } static void add_event_to_list(struct event_list *l, const char* name, const char* provenance, struct kvs *kvs, void* data, event_report_fn f) { if (l->num_events == l->capacity) { l->capacity *= 2; l->events = realloc(l->events, l->capacity * sizeof(struct event)); } l->events[l->num_events].name = name; l->events[l->num_events].provenance = provenance ? provenance : "unknown"; l->events[l->num_events].kvs = kvs; l->events[l->num_events].data = data; l->events[l->num_events].f = f; l->num_events++; } static int report_events_in_list(struct event_list *l, struct str_builder* sb) { int ret = 0; for (int i = 0; i < l->num_events; i++) { if (i != 0) { str_builder_str(sb, ","); } str_builder_str(sb, "{\"name\":"); str_builder_json_str(sb, l->events[i].name); str_builder_str(sb, ",\"provenance\":"); str_builder_json_str(sb, l->events[i].provenance); if (l->events[i].f(sb, l->events[i].data) != 0) { ret = 1; break; } str_builder_str(sb, ",\"details\":"); if (l->events[i].kvs) { kvs_json(l->events[i].kvs, sb); kvs_free(l->events[i].kvs); } else { str_builder_str(sb, "{}"); } str_builder(sb, "}"); } event_list_free(l); event_list_init(l); return ret; } // End of event_list.h