]> git.puffer.fish Git - mirror/frr.git/commitdiff
lib: skiplist: clean up level counter implementation 9646/head
authorG. Paul Ziemba <p-fbsd-bugs@ziemba.us>
Tue, 21 Sep 2021 14:34:20 +0000 (07:34 -0700)
committerG. Paul Ziemba <p-fbsd-bugs@ziemba.us>
Wed, 22 Sep 2021 00:03:53 +0000 (17:03 -0700)
Signed-off-by: G. Paul Ziemba <paulz@labn.net>
lib/skiplist.c
lib/skiplist.h
tests/lib/test_skiplist.c [new file with mode: 0644]
tests/subdir.am

index fc42857418ef1f20e445f4110a0f9938f602320f..c5219f7381f93c81801a7065bccbfcdf9b98a864 100644 (file)
 
 DEFINE_MTYPE_STATIC(LIB, SKIP_LIST, "Skip List");
 DEFINE_MTYPE_STATIC(LIB, SKIP_LIST_NODE, "Skip Node");
+DEFINE_MTYPE_STATIC(LIB, SKIP_LIST_STATS, "Skiplist Counters");
 
 #define BitsInRandom 31
 
 #define MaxNumberOfLevels 16
 #define MaxLevel (MaxNumberOfLevels-1)
-#define newNodeOfLevel(l) XCALLOC(MTYPE_SKIP_LIST_NODE, sizeof(struct skiplistnode)+(l)*sizeof(struct skiplistnode *))
+#define newNodeOfLevel(l)                                                      \
+       XCALLOC(MTYPE_SKIP_LIST_NODE,                                          \
+               sizeof(struct skiplistnode)                                    \
+                       + (l) * sizeof(struct skiplistnode *))
+
+/* XXX must match type of (struct skiplist).level_stats */
+#define newStatsOfLevel(l)                                                     \
+       XCALLOC(MTYPE_SKIP_LIST_STATS, ((l) + 1) * sizeof(int))
 
 static int randomsLeft;
 static int randomBits;
 
-#if 1
+#ifdef SKIPLIST_DEBUG
 #define CHECKLAST(sl)                                                          \
        do {                                                                   \
                if ((sl)->header->forward[0] && !(sl)->last)                   \
@@ -138,7 +146,7 @@ struct skiplist *skiplist_new(int flags,
        new->level = 0;
        new->count = 0;
        new->header = newNodeOfLevel(MaxNumberOfLevels);
-       new->stats = newNodeOfLevel(MaxNumberOfLevels);
+       new->level_stats = newStatsOfLevel(MaxNumberOfLevels);
 
        new->flags = flags;
        if (cmp)
@@ -166,7 +174,7 @@ void skiplist_free(struct skiplist *l)
                p = q;
        } while (p);
 
-       XFREE(MTYPE_SKIP_LIST_NODE, l->stats);
+       XFREE(MTYPE_SKIP_LIST_STATS, l->level_stats);
        XFREE(MTYPE_SKIP_LIST, l);
 }
 
@@ -180,11 +188,13 @@ int skiplist_insert(register struct skiplist *l, register void *key,
 
        CHECKLAST(l);
 
+#ifdef SKIPLIST_DEBUG
        /* DEBUG */
        if (!key) {
                flog_err(EC_LIB_DEVELOPMENT, "%s: key is 0, value is %p",
                         __func__, value);
        }
+#endif
 
        p = l->header;
        k = l->level;
@@ -214,10 +224,10 @@ int skiplist_insert(register struct skiplist *l, register void *key,
        q->flags = SKIPLIST_NODE_FLAG_INSERTED; /* debug */
 #endif
 
-       ++(l->stats->forward[k]);
+       ++(l->level_stats[k]);
 #ifdef SKIPLIST_DEBUG
-       zlog_debug("%s: incremented stats @%p:%d, now %ld", __func__, l, k,
-                  l->stats->forward[k] - (struct skiplistnode *)NULL);
+       zlog_debug("%s: incremented level_stats @%p:%d, now %d", __func__, l, k,
+                  l->level_stats[k]);
 #endif
 
        do {
@@ -298,12 +308,10 @@ int skiplist_delete(register struct skiplist *l, register void *key,
                             k++) {
                                p->forward[k] = q->forward[k];
                        }
-                       --(l->stats->forward[k - 1]);
+                       --(l->level_stats[k - 1]);
 #ifdef SKIPLIST_DEBUG
-                       zlog_debug("%s: decremented stats @%p:%d, now %ld",
-                                  __func__, l, k - 1,
-                                  l->stats->forward[k - 1]
-                                          - (struct skiplistnode *)NULL);
+                       zlog_debug("%s: decremented level_stats @%p:%d, now %d",
+                                  __func__, l, k - 1, l->level_stats[k - 1]);
 #endif
                        if (l->del)
                                (*l->del)(q->value);
@@ -559,11 +567,10 @@ int skiplist_delete_first(register struct skiplist *l)
                l->last = NULL;
        }
 
-       --(l->stats->forward[nodelevel]);
+       --(l->level_stats[nodelevel]);
 #ifdef SKIPLIST_DEBUG
-       zlog_debug("%s: decremented stats @%p:%d, now %ld", __func__, l,
-                  nodelevel,
-                  l->stats->forward[nodelevel] - (struct skiplistnode *)NULL);
+       zlog_debug("%s: decremented level_stats @%p:%d, now %d", __func__, l,
+                  nodelevel, l->level_stats[nodelevel]);
 #endif
 
        if (l->del)
@@ -587,9 +594,7 @@ void skiplist_debug(struct vty *vty, struct skiplist *l)
 
        vty_out(vty, "Skiplist %p has max level %d\n", l, l->level);
        for (i = l->level; i >= 0; --i)
-               vty_out(vty, "  @%d: %ld\n", i,
-                       (long)((l->stats->forward[i])
-                              - (struct skiplistnode *)NULL));
+               vty_out(vty, "  @%d: %d\n", i, l->level_stats[i]);
 }
 
 static void *scramble(int i)
index a106a455d6c271075e754554ed87bc14c734218c..00950e13bbc5fe5292b98b67201e6fe856aecac2 100644 (file)
@@ -60,7 +60,7 @@ struct skiplist {
        int level; /* max lvl (1 + current # of levels in list) */
        unsigned int count;
        struct skiplistnode *header;
-       struct skiplistnode *stats;
+       int *level_stats;
        struct skiplistnode
                *last; /* last real list item (NULL if empty list) */
 
@@ -123,6 +123,7 @@ extern int skiplist_empty(register struct skiplist *l); /* in */
 
 extern unsigned int skiplist_count(register struct skiplist *l); /* in */
 
+struct vty;
 extern void skiplist_debug(struct vty *vty, struct skiplist *l);
 
 extern void skiplist_test(struct vty *vty);
diff --git a/tests/lib/test_skiplist.c b/tests/lib/test_skiplist.c
new file mode 100644 (file)
index 0000000..2f9ca5e
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2021, LabN Consulting, L.L.C
+ *
+ * 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 of the License, 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; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include <skiplist.h>
+
+static void sl_debug(struct skiplist *l)
+{
+       int i;
+
+       if (!l)
+               return;
+
+       printf("Skiplist %p has max level %d\n", l, l->level);
+       for (i = l->level; i >= 0; --i)
+               printf("  @%d: %d\n", i, l->level_stats[i]);
+}
+
+static void *scramble(int i)
+{
+       uintptr_t result;
+
+       result = (uintptr_t)(i & 0xff) << 24;
+       result |= (uintptr_t)i >> 8;
+
+       return (void *)result;
+}
+#define sampleSize 65536
+static int sl_test(void)
+{
+       struct skiplist *l;
+       register int i, k;
+       void *keys[sampleSize];
+       void *v = NULL;
+       int errors = 0;
+
+       l = skiplist_new(SKIPLIST_FLAG_ALLOW_DUPLICATES, NULL, NULL);
+
+       printf("%s: skiplist_new returned %p\n", __func__, l);
+
+       for (i = 0; i < 4; i++) {
+
+               for (k = 0; k < sampleSize; k++) {
+                       if (!(k % 10000))
+                               printf("%s: (%d:%d)\n", __func__, i, k);
+                       /* keys[k] = (void *)random(); */
+                       keys[k] = scramble(k);
+                       if (skiplist_insert(l, keys[k], keys[k])) {
+                               ++errors;
+                               printf("error in insert #%d,#%d\n", i, k);
+                       }
+               }
+
+               printf("%s: inserts done\n", __func__);
+               sl_debug(l);
+
+               for (k = 0; k < sampleSize; k++) {
+
+                       if (!(k % 10000))
+                               printf("[%d:%d]\n", i, k);
+                       /* keys[k] = (void *)random(); */
+                       if (skiplist_search(l, keys[k], &v)) {
+                               ++errors;
+                               printf("error in search #%d,#%d\n", i, k);
+                       }
+
+                       if (v != keys[k]) {
+                               ++errors;
+                               printf("search returned wrong value\n");
+                       }
+               }
+               printf("%s: searches done\n", __func__);
+
+
+               for (k = 0; k < sampleSize; k++) {
+
+                       if (!(k % 10000))
+                               printf("<%d:%d>\n", i, k);
+                       /* keys[k] = (void *)random(); */
+                       if (skiplist_delete(l, keys[k], keys[k])) {
+                               ++errors;
+                               printf("error in delete\n");
+                       }
+                       keys[k] = scramble(k ^ 0xf0f0f0f0);
+                       if (skiplist_insert(l, keys[k], keys[k])) {
+                               ++errors;
+                               printf("error in insert #%d,#%d\n", i, k);
+                       }
+               }
+
+               printf("%s: del+inserts done\n", __func__);
+               sl_debug(l);
+
+               for (k = 0; k < sampleSize; k++) {
+
+                       if (!(k % 10000))
+                               printf("{%d:%d}\n", i, k);
+                       /* keys[k] = (void *)random(); */
+                       if (skiplist_delete_first(l)) {
+                               ++errors;
+                               printf("error in delete_first\n");
+                       }
+               }
+       }
+
+       sl_debug(l);
+
+       skiplist_free(l);
+
+       return errors;
+}
+
+int main(int argc, char **argv)
+{
+       int errors = sl_test();
+
+       if (errors)
+               return 1;
+       return 0;
+}
index 1edfda9bc2cb0649320581f120ccb4080c797d2d..f21e12ecbb38aff48936b20ba7626839241edb05 100644 (file)
@@ -98,6 +98,7 @@ check_PROGRAMS = \
        tests/lib/test_segv \
        tests/lib/test_seqlock \
        tests/lib/test_sig \
+       tests/lib/test_skiplist \
        tests/lib/test_stream \
        tests/lib/test_table \
        tests/lib/test_timer_correctness \
@@ -366,6 +367,10 @@ tests_lib_test_sig_CFLAGS = $(TESTS_CFLAGS)
 tests_lib_test_sig_CPPFLAGS = $(TESTS_CPPFLAGS)
 tests_lib_test_sig_LDADD = $(ALL_TESTS_LDADD)
 tests_lib_test_sig_SOURCES = tests/lib/test_sig.c
+tests_lib_test_skiplist_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_skiplist_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_skiplist_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_skiplist_SOURCES = tests/lib/test_skiplist.c
 tests_lib_test_srcdest_table_CFLAGS = $(TESTS_CFLAGS)
 tests_lib_test_srcdest_table_CPPFLAGS = $(TESTS_CPPFLAGS)
 tests_lib_test_srcdest_table_LDADD = $(ALL_TESTS_LDADD)