((label_pair_t const *)b)->name);
}
-label_pair_t *label_set_get(label_set_t labels, char const *name) {
+static label_pair_t *label_set_read(label_set_t labels, char const *name) {
if (name == NULL) {
errno = EINVAL;
return NULL;
return ret;
}
-int label_set_add(label_set_t *labels, char const *name, char const *value) {
+static int label_set_create(label_set_t *labels, char const *name,
+ char const *value) {
if ((labels == NULL) || (name == NULL) || (value == NULL)) {
return EINVAL;
}
return EINVAL;
}
- if (label_set_get(*labels, name) != NULL) {
+ if (label_set_read(*labels, name) != NULL) {
return EEXIST;
}
+ errno = 0;
if (strlen(value) == 0) {
return 0;
return 0;
}
+static int label_set_delete(label_set_t *labels, label_pair_t *elem) {
+ if ((labels == NULL) || (elem == NULL)) {
+ return EINVAL;
+ }
+
+ if ((elem < labels->ptr) || (elem > labels->ptr + (labels->num - 1))) {
+ return ERANGE;
+ }
+
+ size_t index = elem - labels->ptr;
+ assert(labels->ptr + index == elem);
+
+ free(elem->name);
+ free(elem->value);
+
+ if (index != (labels->num - 1)) {
+ memmove(labels->ptr + index, labels->ptr + (index + 1),
+ labels->num - (index + 1));
+ }
+ labels->num--;
+
+ if (labels->num == 0) {
+ free(labels->ptr);
+ labels->ptr = NULL;
+ }
+
+ return 0;
+}
+
+static void label_set_reset(label_set_t *labels) {
+ if (labels == NULL) {
+ return;
+ }
+ for (size_t i = 0; i < labels->num; i++) {
+ free(labels->ptr[i].name);
+ free(labels->ptr[i].value);
+ }
+ free(labels->ptr);
+
+ labels->ptr = NULL;
+ labels->num = 0;
+}
+
static int label_set_clone(label_set_t *dest, label_set_t src) {
if (src.num == 0) {
return 0;
return 0;
}
-void label_set_reset(label_set_t *labels) {
- if (labels == NULL) {
- return;
- }
- for (size_t i = 0; i < labels->num; i++) {
- free(labels->ptr[i].name);
- free(labels->ptr[i].value);
+int metric_reset(metric_t *m) {
+ if (m == NULL) {
+ return EINVAL;
}
- free(labels->ptr);
- labels->ptr = NULL;
- labels->num = 0;
+ label_set_reset(&m->label);
+ meta_data_destroy(m->meta);
+
+ memset(m, 0, sizeof(*m));
+
+ return 0;
}
int metric_identity(strbuf_t *buf, metric_t const *m) {
return status || strbuf_print(buf, "}");
}
+int metric_label_set(metric_t *m, char const *name, char const *value) {
+ if ((m == NULL) || (name == NULL)) {
+ return EINVAL;
+ }
+
+ label_pair_t *label = label_set_read(m->label, name);
+ if ((label == NULL) && (errno != ENOENT)) {
+ return errno;
+ }
+ errno = 0;
+
+ if (label == NULL) {
+ if ((value == NULL) || strlen(value) == 0) {
+ return 0;
+ }
+ return label_set_create(&m->label, name, value);
+ }
+
+ if ((value == NULL) || strlen(value) == 0) {
+ return label_set_delete(&m->label, label);
+ }
+
+ char *new_value = strdup(value);
+ if (new_value == NULL) {
+ return errno;
+ }
+
+ free(label->value);
+ label->value = new_value;
+
+ return 0;
+}
+
+char const *metric_label_get(metric_t const *m, char const *name) {
+ if ((m == NULL) || (name == NULL)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ label_pair_t *set = label_set_read(m->label, name);
+ if (set == NULL) {
+ return NULL;
+ }
+
+ return set->value;
+}
+
int metric_list_add(metric_list_t *metrics, metric_t m) {
if (metrics == NULL) {
return EINVAL;
}
metrics->ptr = tmp;
- metrics->ptr[metrics->num] = m;
+ metric_t copy = {
+ .family = m.family,
+ .value = m.value,
+ .time = m.time,
+ .interval = m.interval,
+ .meta = meta_data_clone(m.meta),
+ };
+ int status = label_set_clone(©.label, m.label);
+ if (((m.meta != NULL) && (copy.meta == NULL)) || (status != 0)) {
+ label_set_reset(©.label);
+ meta_data_destroy(copy.meta);
+ return status;
+ }
+
+ metrics->ptr[metrics->num] = copy;
metrics->num++;
return 0;
for (size_t i = 0; i < metrics->num; i++) {
label_set_reset(&metrics->ptr[i].label);
+ meta_data_destroy(metrics->ptr[i].meta);
}
free(metrics->ptr);
};
for (size_t i = 0; i < label_num; i++) {
- int status = label_set_add(&m.label, label[i].name, label[i].value);
+ int status = label_set_create(&m.label, label[i].name, label[i].value);
if (status != 0) {
label_set_reset(&m.label);
return status;
/* one metric is added to the family by metric_family_unmarshal_text. */
assert(fam->metric.num >= 1);
- status = label_set_add(&fam->metric.ptr[0].label, key, value.ptr);
+ status = metric_label_set(m, key, value.ptr);
STRBUF_DESTROY(value);
if (status != 0) {
ret = status;
char *value;
} label_pair_t;
-/* label_t represents a constant label, i.e. a key/value pair. It is similar to
- * label_pair_t, except that is has const fields. label_t is used in function
- * arguments to prevent the called function from modifying its argument.
- * Internally labels are stored as label_pair_t to allow modification, e.g. by
- * targets in the "filter chain". */
+/* label_t represents a key/value pair. It is similar to label_pair_t, except
+ * that is has const fields. label_t is used in function arguments to prevent
+ * the called function from modifying its argument. Internally labels are
+ * stored as label_pair_t to allow modification, e.g. by targets in the "filter
+ * chain". */
typedef struct {
char const *name;
char const *value;
size_t num;
} label_set_t;
-/* label_set_get efficiently looks up and returns the "name" label. If a label
- * does not exist, NULL is returned and errno is set to ENOENT. */
-label_pair_t *label_set_get(label_set_t labels, char const *name);
-
-/* label_set_add adds a new label to the set of labels. If a label with "name"
- * already exists, EEXIST is returned. If "value" is the empty string, no label
- * is added and zero is returned. */
-int label_set_add(label_set_t *labels, char const *name, char const *value);
-
-/* label_set_reset frees all the labels in the label set. It does *not* free
- * the passed "label_set_t*" itself. */
-void label_set_reset(label_set_t *labels);
-
/*
* Metric
*/
cdtime_t time; /* TODO(octo): use ms or µs instead? */
cdtime_t interval;
/* TODO(octo): target labels */
- meta_data_t *meta; /* TODO(octo): free in metric_list_reset() */
+ meta_data_t *meta;
} metric_t;
/* metric_identity writes the identity of the metric "m" to "buf". An example
* be freed by passing m->family to metric_family_free(). */
metric_t *metric_parse_identity(char const *s);
+/* metric_label_set adds or updates a label to/in the label set.
+ * If "value" is NULL or the empty string, the label is removed. Removing a
+ * label that does not exist is *not* an error. */
+int metric_label_set(metric_t *m, char const *name, char const *value);
+
+/* metric_label_get efficiently looks up and returns the value of the "name"
+ * label. If a label does not exist, NULL is returned and errno is set to
+ * ENOENT. The returned pointer may not be valid after a subsequent call to
+ * "metric_label_set". */
+char const *metric_label_get(metric_t const *m, char const *name);
+
+/* metric_reset frees all labels and meta data stored in the metric and resets
+ * the metric to zero. */
+int metric_reset(metric_t *m);
+
/* metric_list_t is an unordered list of metrics. */
typedef struct {
metric_t *ptr;
size_t num;
} metric_list_t;
-/* metric_list_add appends a metric to the metric list. */
+/* metric_list_add appends a metric to the metric list. The metric's labels and
+ * meta data are copied. */
int metric_list_add(metric_list_t *metrics, metric_t m);
/* metric_list_reset frees all the metrics in the metric list. It does *not*
#include "metric.h"
#include "testing.h"
+DEF_TEST(metric_label_set) {
+ struct {
+ char const *key;
+ char const *value;
+ int want_err;
+ char const *want_get;
+ } cases[] = {
+ {
+ .key = "foo",
+ .value = "bar",
+ .want_get = "bar",
+ },
+ {
+ .key = NULL,
+ .value = "bar",
+ .want_err = EINVAL,
+ },
+ {
+ .key = "foo",
+ .value = NULL,
+ },
+ {
+ .key = "",
+ .value = "bar",
+ .want_err = EINVAL,
+ },
+ {
+ .key = "valid",
+ .value = "",
+ },
+ {
+ .key = "1nvalid",
+ .value = "bar",
+ .want_err = EINVAL,
+ },
+ {
+ .key = "val1d",
+ .value = "bar",
+ .want_get = "bar",
+ },
+ {
+ .key = "inva!id",
+ .value = "bar",
+ .want_err = EINVAL,
+ },
+ };
+
+ for (size_t i = 0; i < (sizeof(cases) / sizeof(cases[0])); i++) {
+ printf("## Case %zu: %s=\"%s\"\n", i,
+ cases[i].key ? cases[i].key : "(null)",
+ cases[i].value ? cases[i].value : "(null)");
+
+ metric_t m = {0};
+
+ EXPECT_EQ_INT(cases[i].want_err,
+ metric_label_set(&m, cases[i].key, cases[i].value));
+ EXPECT_EQ_STR(cases[i].want_get, metric_label_get(&m, cases[i].key));
+
+ metric_reset(&m);
+ EXPECT_EQ_PTR(NULL, m.label.ptr);
+ EXPECT_EQ_INT(0, m.label.num);
+ }
+
+ return 0;
+}
+
DEF_TEST(metric_identity) {
struct {
char *name;
}
int main(void) {
+ RUN_TEST(metric_label_set);
RUN_TEST(metric_identity);
END_TEST;