]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
src/daemon/metric.[ch]: Add metric_label_{get,set,reset}().
authorFlorian Forster <octo@google.com>
Sun, 28 Jun 2020 19:07:27 +0000 (21:07 +0200)
committerFlorian Forster <octo@google.com>
Tue, 21 Jul 2020 15:29:12 +0000 (17:29 +0200)
label_set_{get,add,reset} have been made static, providing the calling
code with a nicer API.

src/daemon/metric.c
src/daemon/metric.h
src/daemon/metric_test.c

index b0a37fd103765a03f32097bbb1ee57142ab88333..1890fbf9c41dc38f783a1cc20a21805006498950 100644 (file)
@@ -60,7 +60,7 @@ static int label_pair_compare(void const *a, void const *b) {
                 ((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;
@@ -80,7 +80,8 @@ label_pair_t *label_set_get(label_set_t labels, char const *name) {
   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;
   }
@@ -95,9 +96,10 @@ int label_set_add(label_set_t *labels, char const *name, char const *value) {
     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;
@@ -127,6 +129,49 @@ int label_set_add(label_set_t *labels, char const *name, char const *value) {
   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;
@@ -153,18 +198,17 @@ static int label_set_clone(label_set_t *dest, label_set_t src) {
   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) {
@@ -193,6 +237,53 @@ 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;
@@ -205,7 +296,21 @@ int metric_list_add(metric_list_t *metrics, metric_t m) {
   }
   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(&copy.label, m.label);
+  if (((m.meta != NULL) && (copy.meta == NULL)) || (status != 0)) {
+    label_set_reset(&copy.label);
+    meta_data_destroy(copy.meta);
+    return status;
+  }
+
+  metrics->ptr[metrics->num] = copy;
   metrics->num++;
 
   return 0;
@@ -251,6 +356,7 @@ void metric_list_reset(metric_list_t *metrics) {
 
   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);
 
@@ -270,7 +376,7 @@ int metric_family_metrics_append(metric_family_t *fam, value_t v,
   };
 
   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;
@@ -441,7 +547,7 @@ static int metric_family_unmarshal_identity(metric_family_t *fam,
     /* 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;
index b3b5f674dd867df04e1efab99f19e90718c0607f..1a6f9b210a364977dc8174689f335f8fe4e1f94b 100644 (file)
@@ -64,11 +64,11 @@ typedef struct {
   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;
@@ -80,19 +80,6 @@ typedef struct {
   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
  */
@@ -110,7 +97,7 @@ typedef struct {
   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
@@ -125,13 +112,29 @@ int metric_identity(strbuf_t *buf, metric_t const *m);
  * 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*
index 36ace473eb58a71a4da884ee2e1002a39c7cece3..da7b4731b1211d3cb5689bad7a7c5599d8074f79 100644 (file)
 #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;
@@ -94,6 +160,7 @@ DEF_TEST(metric_identity) {
 }
 
 int main(void) {
+  RUN_TEST(metric_label_set);
   RUN_TEST(metric_identity);
 
   END_TEST;