summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2014-03-17 19:18:48 +0100
committerThomas Haller <thaller@redhat.com>2014-03-27 12:56:26 +0100
commite65e64542baa07bce6ecaf08e415a21ae1f79daf (patch)
tree26c259b10bcd0ccab0c183c15f3bc52b5294187d
parent4dba720d8c883549e57a292a236c8ea44e1e0bca (diff)
downloadNetworkManager-th/wip/NMStringIndex.tar.gz
libnl-util: add NMStringTableth/wip/NMStringIndexth/NMStringIndex
Signed-off-by: Thomas Haller <thaller@redhat.com>
-rw-r--r--.gitignore1
-rw-r--r--libnm-util/Makefile.am4
-rw-r--r--libnm-util/nm-string-index.c377
-rw-r--r--libnm-util/nm-string-index.h58
-rw-r--r--libnm-util/tests/Makefile.am11
-rw-r--r--libnm-util/tests/test-nm-string-index.c152
6 files changed, 601 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index f9401dabca..c986346643 100644
--- a/.gitignore
+++ b/.gitignore
@@ -174,6 +174,7 @@ valgrind-*.log
/libnm-util/tests/test-settings-defaults
/libnm-util/tests/test-general
/libnm-util/tests/test-need-secrets
+/libnm-util/tests/test-nm-string-index
/libnm-util/tests/test-secrets
/libnm-util/tests/test-setting-8021x
/libnm-util/tests/test-setting-dcb
diff --git a/libnm-util/Makefile.am b/libnm-util/Makefile.am
index 84f4aa303d..3fca7e94d9 100644
--- a/libnm-util/Makefile.am
+++ b/libnm-util/Makefile.am
@@ -58,7 +58,8 @@ libnm_util_la_private_headers = \
nm-param-spec-specialized.h \
nm-util-private.h \
nm-utils-private.h \
- nm-setting-private.h
+ nm-setting-private.h \
+ nm-string-index.h
libnm_util_la_csources = \
crypto.c \
@@ -91,6 +92,7 @@ libnm_util_la_csources = \
nm-setting-wireless.c \
nm-setting-wireless-security.c \
nm-setting-vpn.c \
+ nm-string-index.c \
nm-util-private.c \
nm-utils-enum-types.c \
nm-utils.c \
diff --git a/libnm-util/nm-string-index.c b/libnm-util/nm-string-index.c
new file mode 100644
index 0000000000..ad5c049763
--- /dev/null
+++ b/libnm-util/nm-string-index.c
@@ -0,0 +1,377 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2014 Red Hat, Inc.
+ */
+
+#include "nm-string-index.h"
+
+#include <string.h>
+
+const NMStringIndexCompareFunc COMPARE_DEFAULT = &strcmp;
+
+typedef struct NMStringIndex {
+ guint size;
+ NMStringIndexCompareFunc compare;
+
+ void *_offset;
+} NMStringIndex;
+
+#define OFFSET_STRING(table) ((const char **) ((&((table)->_offset))))
+#define OFFSET_DATA(table) ((void **) &(OFFSET_STRING (table)[(table)->size + 1]))
+#define OFFSET_STATICSTR(table) ((const char *) &(OFFSET_DATA (table)[(table)->size + 1]))
+
+#ifndef G_DISABLE_ASSERT
+struct prepare_sort_data
+{
+ NMStringIndexCompareFunc compare;
+ gboolean duplicate;
+};
+
+static gint
+prepare_sort_check (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ const NMStringIndexItem *a1 = a;
+ const NMStringIndexItem *a2 = b;
+ struct prepare_sort_data *data = user_data;
+ int c;
+
+ c = (data->compare) (a1->key, a2->key);
+ if (c == 0)
+ data->duplicate = TRUE;
+ return c;
+}
+#else
+static gint
+prepare_sort_direct (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ const NMStringIndexItem *a1 = a;
+ const NMStringIndexItem *a2 = b;
+
+ return ((NMStringIndexCompareFunc) user_data) (a1->key, a2->key);
+}
+#endif
+
+static NMStringIndex *
+_new (NMStringIndexCompareFunc compare, GArray *prepare)
+{
+ NMStringIndex *self;
+ guint i;
+ const char **tab_str;
+ const char *tab_static;
+ void **tab_dat;
+ size_t len;
+ GString *static_strings;
+ guint argc = prepare ? prepare->len : 0;
+
+ if (!compare)
+ compare = COMPARE_DEFAULT;
+
+ if (argc > G_MAXINT) {
+ g_array_free (prepare, TRUE);
+ g_return_val_if_reached (NULL);
+ return NULL;
+ }
+
+ static_strings = g_string_new ("");
+
+ if (prepare) {
+#ifndef G_DISABLE_ASSERT
+ struct prepare_sort_data sort_data = { .compare = compare };
+
+ g_array_sort_with_data (prepare, prepare_sort_check, &sort_data);
+ if (sort_data.duplicate) {
+ g_string_free (static_strings, TRUE);
+ g_array_free (prepare, TRUE);
+ g_return_val_if_reached (NULL);
+ }
+#else
+ g_array_sort_with_data (prepare, prepare_sort_direct, compare);
+#endif
+
+ for (i = 0; i < argc; i++) {
+ NMStringIndexItem *data = &g_array_index (prepare, NMStringIndexItem, i);
+ char *new_key = &static_strings->str[static_strings->len];
+
+ g_string_append (static_strings, data->key);
+ g_string_append_c (static_strings, '\0');
+ data->key = new_key;
+ }
+ } else
+ g_string_append_c (static_strings, '\0');
+
+ len = offsetof (NMStringIndex, _offset) +
+ (argc + 1) * (sizeof (const char *) + sizeof (void *)) +
+ static_strings->len;
+
+ self = g_malloc (len);
+ self->size = argc;
+ self->compare = compare;
+
+ tab_str = OFFSET_STRING (self);
+ tab_dat = OFFSET_DATA (self);
+ tab_static = OFFSET_STATICSTR (self);
+ memcpy ((char *) tab_static, static_strings->str, static_strings->len);
+ for (i = 0; i < argc; i++) {
+ NMStringIndexItem *data = &g_array_index (prepare, NMStringIndexItem, i);
+
+ (*tab_str++) = &tab_static[data->key - static_strings->str];
+ (*tab_dat++) = data->data;
+ }
+ *tab_str = NULL;
+ *tab_dat = NULL;
+
+ g_string_free (static_strings, TRUE);
+
+ if (prepare)
+ g_array_free (prepare, TRUE);
+
+ return self;
+}
+
+#ifndef G_DISABLE_ASSERT
+static gboolean
+_assert_args (GArray *prepare, int argc)
+{
+ guint i;
+
+ g_return_val_if_fail (prepare, FALSE);
+ g_return_val_if_fail (prepare->len == argc, FALSE);
+
+ for (i = 0; i < prepare->len; i++ )
+ g_return_val_if_fail (g_array_index (prepare, NMStringIndexItem, i).key, FALSE);
+ return TRUE;
+}
+#define ASSERT_ARGS(prepare, argc) \
+ G_STMT_START { \
+ if (!_assert_args (prepare, argc)) { \
+ g_array_free (prepare, TRUE); \
+ return NULL; \
+ } \
+ } G_STMT_END
+#else
+#define ASSERT_ARGS(prepare, argc) G_STMT_START { ; } G_STMT_END
+#endif
+
+NMStringIndex *
+nm_string_index_new_keys_only (NMStringIndexCompareFunc compare, int argc, const char **args)
+{
+ guint i;
+ GArray *prepare = NULL;
+
+ g_return_val_if_fail ((argc > 0 && args) || argc <= 0, NULL);
+
+ if (argc > 0 || args) {
+ NMStringIndexItem data = { 0 };
+
+ prepare = g_array_sized_new (FALSE, FALSE, sizeof (NMStringIndexItem), argc > 0 ? argc : 0);
+ if (argc > 0) {
+ for (i = 0; i < argc; i++) {
+ data.key = args[i];
+ g_array_append_val (prepare, data);
+ }
+ ASSERT_ARGS (prepare, argc);
+ } else if (argc < 0) {
+ /* argc is negative, that means that args is NULL terminated. */
+ for (; *args; args++) {
+ data.key = *args;
+ g_array_append_val (prepare, data);
+ }
+ }
+ }
+ return _new (compare, prepare);
+}
+
+NMStringIndex *
+nm_string_index_new (NMStringIndexCompareFunc compare, int argc, const NMStringIndexItem *args)
+{
+ GArray *prepare = NULL;
+
+ g_return_val_if_fail (!argc || args, NULL);
+
+ if (argc > 0 || args) {
+ prepare = g_array_sized_new (FALSE, FALSE, sizeof (NMStringIndexItem), 0);
+ if (argc > 0) {
+ g_array_append_vals (prepare, args, argc);
+ ASSERT_ARGS (prepare, argc);
+ } else if (argc < 0) {
+ /* argc is negative, that means that args is NULL terminated. */
+ for (; args->key; args++)
+ g_array_append_val (prepare, *args);
+ }
+ }
+ return _new (compare, prepare);
+}
+
+
+guint
+nm_string_index_size (NMStringIndex *self)
+{
+ g_return_val_if_fail (self, 0);
+
+ return self->size;
+}
+
+static int
+_lookup_by_key (NMStringIndex *self, const char *key)
+{
+ const char *const*t_str;
+ guint imin, imax, imid;
+
+ g_return_val_if_fail (self, -1);
+ g_return_val_if_fail (key, -1);
+
+ if (self->size == 0)
+ return -1;
+
+ t_str = OFFSET_STRING (self);
+ imin = 0;
+ imax = self->size - 1;
+
+ if (key >= t_str[0] && key <= t_str[imax]) {
+ /* Try binary search using the pointer value. We try this
+ * optimization, because the user might lookup using one of the
+ * key values themselves.
+ */
+ while (imax >= imin) {
+ /* save against overflow, because size is <= INT_MAX */
+ imid = (imax + imin) / 2;
+ if (t_str[imid] == key)
+ return imid;
+ if (t_str[imid] < key)
+ imin = imid + 1;
+ else
+ imax = imid - 1;
+ }
+
+ /* if we did not find by pointer comparison, try again using
+ * strcmp. The user has provided a substring to one of the keys...
+ */
+ imin = 0;
+ imax = self->size - 1;
+ }
+ /* binary search using the compare function. */
+ while (imax >= imin) {
+ int c;
+
+ /* save against overflow, because size is <= INT_MAX */
+ imid = (imax + imin) / 2;
+ c = self->compare (key, t_str[imid]);
+
+ if (!c)
+ return imid;
+ if (c > 0)
+ imin = imid + 1;
+ else
+ imax = imid - 1;
+ }
+
+ return -1;
+}
+
+static gboolean
+_access_at_index (NMStringIndex *self, int idx, const char **out_key, gpointer **out_data)
+{
+ if (idx < 0) {
+ if (out_key)
+ *out_key = NULL;
+ if (out_data)
+ out_data = NULL;
+ return FALSE;
+ }
+ if (out_key)
+ *out_key = OFFSET_STRING (self)[idx];
+ if (out_data)
+ *out_data = &OFFSET_DATA (self)[idx];
+ return TRUE;
+}
+
+gboolean
+nm_string_index_lookup_by_key (NMStringIndex *self, const char *key, int *out_idx, const char **out_key, gpointer **out_data)
+{
+ int idx = _lookup_by_key (self, key);
+
+ if (out_idx)
+ *out_idx = idx;
+ return _access_at_index (self, idx, out_key, out_data);
+}
+
+gboolean
+nm_string_index_lookup_by_index (NMStringIndex *self, int idx, const char **out_key, gpointer **out_data)
+{
+ g_return_val_if_fail (self, FALSE);
+
+ if (idx >= self->size) {
+ /* don't check for idx<0, _access_at_index will handle that. */
+ idx = -1;
+ }
+ return _access_at_index (self, idx, out_key, out_data);
+}
+
+gpointer
+nm_string_index_get_data_by_key (NMStringIndex *self, const char *key)
+{
+ int idx = _lookup_by_key (self, key);
+
+ return idx >= 0 ? OFFSET_DATA (self)[idx] : NULL;
+}
+
+const char *const*
+nm_string_index_get_keys (NMStringIndex *self)
+{
+ g_return_val_if_fail (self, NULL);
+
+ return OFFSET_STRING (self);
+}
+
+gpointer *
+nm_string_index_get_data (NMStringIndex *self)
+{
+ g_return_val_if_fail (self, NULL);
+
+ return OFFSET_DATA (self);
+}
+
+NMStringIndexCompareFunc
+nm_string_index_get_compare_func (NMStringIndex *self)
+{
+ g_return_val_if_fail (self, NULL);
+
+ return self->compare;
+}
+
+void
+nm_string_index_foreach (NMStringIndex *self, NMStringIndexForeachFunc func, void *user_data)
+{
+ const char **tab_str;
+ void **tab_dat;
+ guint i, size;
+
+ g_return_if_fail (self);
+ g_return_if_fail (func);
+
+ tab_str = OFFSET_STRING (self);
+ tab_dat = OFFSET_DATA (self);
+ for (i = 0, size = self->size; i < size; i++) {
+ if (!func (tab_str[i], &tab_dat[i], i, user_data))
+ return;
+ }
+}
+
diff --git a/libnm-util/nm-string-index.h b/libnm-util/nm-string-index.h
new file mode 100644
index 0000000000..63eb77fa00
--- /dev/null
+++ b/libnm-util/nm-string-index.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2014 Red Hat, Inc.
+ */
+
+#ifndef __NM_STRING_INDEX_H__
+#define __NM_STRING_INDEX_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct NMStringIndex NMStringIndex;
+
+typedef int (*NMStringIndexCompareFunc) (const char *s1, const char *s2);
+typedef gboolean (*NMStringIndexForeachFunc) (const char *key, gpointer *pdata, int idx, void *user_data);
+
+typedef struct {
+ const char *key;
+ void *data;
+} NMStringIndexItem;
+
+NMStringIndex *nm_string_index_new (NMStringIndexCompareFunc compare, int argc, const NMStringIndexItem *args);
+NMStringIndex *nm_string_index_new_keys_only (NMStringIndexCompareFunc compare, int argc, const char **args);
+
+guint nm_string_index_size (NMStringIndex *table);
+NMStringIndexCompareFunc nm_string_index_get_compare_func (NMStringIndex *table);
+gboolean nm_string_index_lookup_by_key (NMStringIndex *table, const char *key, int *out_idx, const char **out_key, gpointer **out_data);
+gboolean nm_string_index_lookup_by_index (NMStringIndex *table, int idx, const char **out_key, gpointer **out_data);
+
+gpointer nm_string_index_get_data_by_key (NMStringIndex *table, const char *key);
+
+const char *const*nm_string_index_get_keys (NMStringIndex *table);
+gpointer *nm_string_index_get_data (NMStringIndex *table);
+
+void nm_string_index_foreach (NMStringIndex *table, NMStringIndexForeachFunc func, void *user_data);
+
+G_END_DECLS
+
+#endif /* __NM_STRING_INDEX_H__ */
diff --git a/libnm-util/tests/Makefile.am b/libnm-util/tests/Makefile.am
index 0ef1bc007e..de9c72832d 100644
--- a/libnm-util/tests/Makefile.am
+++ b/libnm-util/tests/Makefile.am
@@ -17,6 +17,7 @@ noinst_PROGRAMS = \
test-crypto \
test-secrets \
test-general \
+ test-nm-string-index \
test-setting-8021x \
test-setting-dcb
@@ -52,6 +53,13 @@ test_general_LDADD = \
$(GLIB_LIBS) \
$(DBUS_LIBS)
+test_nm_string_index_SOURCES = \
+ test-nm-string-index.c \
+ $(top_builddir)/libnm-util/nm-string-index.c
+
+test_nm_string_index_LDADD = \
+ $(GLIB_LIBS)
+
test_setting_8021x_SOURCES = \
test-setting-8021x.c
@@ -68,10 +76,11 @@ test_setting_dcb_LDADD = \
$(GLIB_LIBS) \
$(DBUS_LIBS)
-check-local: test-settings-defaults test-crypto test-secrets test-setting-8021x test-setting-dcb
+check-local: test-settings-defaults test-crypto test-secrets test-setting-8021x test-setting-dcb test-nm-string-index
$(abs_builddir)/test-settings-defaults
$(abs_builddir)/test-secrets
$(abs_builddir)/test-general
+ $(abs_builddir)/test-nm-string-index
$(abs_builddir)/test-setting-dcb
# Private key and CA certificate in the same file (PEM)
diff --git a/libnm-util/tests/test-nm-string-index.c b/libnm-util/tests/test-nm-string-index.c
new file mode 100644
index 0000000000..a7b709cebe
--- /dev/null
+++ b/libnm-util/tests/test-nm-string-index.c
@@ -0,0 +1,152 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ */
+
+#include "nm-string-index.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <string.h>
+
+static void
+assert_table (NMStringIndex *table, guint size)
+{
+ const char *const* keys;
+ guint i, j;
+ NMStringIndexCompareFunc compare;
+ gpointer *data;
+ gboolean success;
+
+ g_assert (table);
+ g_assert_cmpint (size, ==, nm_string_index_size (table));
+
+ compare = nm_string_index_get_compare_func (table);
+ g_assert (compare);
+
+ keys = nm_string_index_get_keys (table);
+ data = nm_string_index_get_data (table);
+ g_assert (keys);
+ g_assert (data);
+ for (i = 0; i < size; i++) {
+ g_assert (keys[i]);
+ g_assert (data[i]);
+ if (i > 0) {
+ g_assert (keys[i-1] < keys[i]);
+ g_assert (keys[i] == &(keys[i-1][strlen (keys[i-1]) + 1]));
+ g_assert_cmpint (compare (keys[i-1], keys[i]), <, 0);
+
+ g_assert_cmpint (GPOINTER_TO_INT (data[i-1]), ==, GPOINTER_TO_INT (data[i]) - 1);
+ }
+
+ for (j = 0; ; j++) {
+ const char *l_key;
+ char *tmp_release = NULL;
+ int out_idx = 4711 + j;
+ const char *out_key = "axb";
+ gpointer *out_data = GINT_TO_POINTER (2324+j);
+
+ if (j == 0) {
+ l_key = keys[i];
+ } else if (j == 1) {
+ l_key = tmp_release = g_strdup (keys[i]);
+ } else if(j == 2 && strcmp (keys[i], "SUB") == 0) {
+ success = nm_string_index_lookup_by_key (table, "SUBSUB", NULL, &l_key, NULL);
+ g_assert (success);
+ l_key = &l_key[3];
+ } else
+ break;
+ success = nm_string_index_lookup_by_key (table, l_key, &out_idx, &out_key, &out_data);
+ g_assert (success);
+ g_assert_cmpint (i, ==, out_idx);
+ g_assert (&data[i] == out_data);
+ g_assert (keys[i] == out_key);
+
+ g_assert (data[i] == nm_string_index_get_data_by_key (table, l_key));
+
+ g_free (tmp_release);
+
+ if (j == 0) {
+ success = nm_string_index_lookup_by_index (table, i, &out_key, &out_data);
+ g_assert (success);
+ g_assert (&data[i] == out_data);
+ g_assert (keys[i] == out_key);
+ }
+ }
+ }
+ g_assert (!keys[size]);
+ g_assert (!data[size]);
+}
+
+static void
+test_run ()
+{
+ NMStringIndex *table;
+ const NMStringIndexItem args1[] = {
+ {
+ .key = "c",
+ .data = GINT_TO_POINTER (5),
+ },
+ {
+ .key = "SUBSUB",
+ .data = GINT_TO_POINTER (2),
+ },
+ {
+ .key = "SUB",
+ .data = GINT_TO_POINTER (1),
+ },
+ {
+ .key = "b",
+ .data = GINT_TO_POINTER (4),
+ },
+ {
+ .key = "a",
+ .data = GINT_TO_POINTER (3),
+ },
+ {
+ .key = "dddc",
+ .data = GINT_TO_POINTER (6),
+ },
+ {
+ .key = "dddd",
+ .data = GINT_TO_POINTER (7),
+ },
+ { 0 },
+ };
+
+ table = nm_string_index_new (NULL, -1, args1);
+ assert_table (table, 7);
+
+ g_free (table);
+}
+
+#define TPATH "/libnm-util/nm-string-index/"
+
+int main (int argc, char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+ g_type_init ();
+
+ g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL);
+
+ g_test_add_func (TPATH "run", test_run);
+
+ return g_test_run ();
+}
+