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) \
new->level = 0;
new->count = 0;
new->header = newNodeOfLevel(MaxNumberOfLevels);
- new->stats = newNodeOfLevel(MaxNumberOfLevels);
+ new->level_stats = newStatsOfLevel(MaxNumberOfLevels);
new->flags = flags;
if (cmp)
p = q;
} while (p);
- XFREE(MTYPE_SKIP_LIST_NODE, l->stats);
+ XFREE(MTYPE_SKIP_LIST_STATS, l->level_stats);
XFREE(MTYPE_SKIP_LIST, l);
}
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;
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 {
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);
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)
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)
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) */
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);
--- /dev/null
+/*
+ * 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;
+}
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 \
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)