integrity p2
This commit is contained in:
parent
64ac937a6a
commit
35f04e9d59
|
@ -1,15 +1,18 @@
|
|||
#include <sys/time.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define DEBUG 1
|
||||
#define SKIPLIST_DEBUG slex
|
||||
|
||||
/* Setting this will do two things:
|
||||
* 1) limit our max height across all instances of this datastructure.
|
||||
* 2) remove a heap allocation on frequently used paths, insert/remove/etc.
|
||||
|
|
251
include/sl.h
251
include/sl.h
|
@ -139,6 +139,21 @@
|
|||
#define SKIPLIST_MAX_HEIGHT 1
|
||||
#endif
|
||||
|
||||
#if defined(SKIPLIST_DEBUG)
|
||||
#ifndef SKIP_DEBUGF
|
||||
#define SKIP_DEBUGF
|
||||
#if defined(DEBUG) && DEBUG > 0
|
||||
#define __skip_debugf(...) \
|
||||
do { \
|
||||
fprintf(stderr, "%s:%d:%s(): ", __FILE__, __LINE__, __func__); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define __skip_debugf(fmt, args...) ((void)0)
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* A Skip List contains elements, a portion of which is used to manage those
|
||||
* elements while the rest is defined by the use case for this declaration. The
|
||||
|
@ -971,112 +986,136 @@
|
|||
return NULL; \
|
||||
}
|
||||
|
||||
#define SKIPLIST_INTEGRITY_CHECK(decl, prefix, field) \
|
||||
/* -- __skip_integrity_check_ */ \
|
||||
static int __skip_integrity_check_##decl(decl##_t *slist, int flags) \
|
||||
{ \
|
||||
unsigned nth, n_err = 0; \
|
||||
size_t size; \
|
||||
FILE *of = stderr; \
|
||||
decl##_node_t *node; \
|
||||
struct __skiplist_##decl_idx *this, *that, *prev, *next; \
|
||||
\
|
||||
if (slist == NULL) { \
|
||||
fprintf(of, "slist was NULL, nothing to check"); \
|
||||
n_err++; \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
/* Check the Skiplist header (slh) */ \
|
||||
\
|
||||
if (slist->slh_head == NULL) { \
|
||||
fprintf(of, "skiplist slh_head is NULL"); \
|
||||
n_err++; \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (slist->slh_tail == NULL) { \
|
||||
fprintf(of, "skiplist slh_tail is NULL"); \
|
||||
n_err++; \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (slist->cmp == NULL) { \
|
||||
fprintf(of, "skiplist comparison function (cmp) is NULL"); \
|
||||
n_err++; \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (slist->level >= slist->max) { \
|
||||
/* level is 0-based, max of 12 means level cannot be > 11 */ \
|
||||
fprintf(of, "skiplist level in header was >= max"); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (SKIPLIST_MAX_HEIGHT < 1) { \
|
||||
fprintf(of, "SKIPLIST_MAX_HEIGHT cannot be less than 1"); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (SKIPLIST_MAX_HEIGHT > 0 && slist->max > SKIPLIST_MAX_HEIGHT) { \
|
||||
fprintf(of, "slist->max cannot be greater than SKIPLIST_MAX_HEIGHT"); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (slist->length > 0 && slist->slh_head->field.sle.next[0] != slist->slh_tail) { \
|
||||
fprintf(of, "slist->length is 0, but head->next == tail, not an internal node"); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (slist->length > 0 && slist->slh_tail->field.sle.prev != slist->slh_head) { \
|
||||
fprintf(of, "slist->length is 0, but tail->prev == head, not an internal node"); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
/* Validate the head node */ \
|
||||
\
|
||||
/* Validate the tail node */ \
|
||||
\
|
||||
/* Validate each node */ \
|
||||
nth = 0; \
|
||||
size = prefix##skip_size_##decl(slist); \
|
||||
node = prefix##skip_head_##decl(slist); \
|
||||
do { \
|
||||
this = &node->field.sle; \
|
||||
if (this->next == NULL) { \
|
||||
fprintf(of, "the %u node's [%p] next field should never NULL", nth, (void *)node); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
if (this->prev == NULL) { \
|
||||
fprintf(of, "the %u node [%p] prev field should never NULL", nth, (void *)node); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
if (*this->next != node + (sizeof(struct __skiplist_##decl_idx) * slist->max)) { \
|
||||
fprintf(of, "the %u node's [%p] next field isn't at the proper offset relative to the node", nth, (void *)node); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
node = prefix##skip_next_node_##decl(slist, node); \
|
||||
nth++; \
|
||||
} while (node != NULL); \
|
||||
\
|
||||
return 0; \
|
||||
#define SKIPLIST_INTEGRITY_CHECK(decl, prefix, field) \
|
||||
/* -- __skip_integrity_failure_ */ \
|
||||
static void __attribute__((format(printf, 1, 2))) __skip_integrity_failure_##decl(const char *fmt, ...) \
|
||||
{ \
|
||||
va_list args; \
|
||||
__skip_debugf(fmt, args); \
|
||||
} \
|
||||
\
|
||||
/* -- __skip_integrity_check_ */ \
|
||||
static int __skip_integrity_check_##decl(decl##_t *slist, int flags) \
|
||||
{ \
|
||||
size_t size; \
|
||||
unsigned nth, n_err = 0; \
|
||||
decl##_node_t *node; \
|
||||
struct __skiplist_##decl_idx *this, *that, *prev, *next; \
|
||||
\
|
||||
if (slist == NULL) { \
|
||||
__skip_integrity_failure_##decl("slist was NULL, nothing to check"); \
|
||||
n_err++; \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
/* Check the Skiplist header (slh) */ \
|
||||
\
|
||||
if (slist->slh_head == NULL) { \
|
||||
__skip_integrity_failure_##decl("skiplist slh_head is NULL"); \
|
||||
n_err++; \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (slist->slh_tail == NULL) { \
|
||||
__skip_integrity_failure_##decl("skiplist slh_tail is NULL"); \
|
||||
n_err++; \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (slist->cmp == NULL) { \
|
||||
__skip_integrity_failure_##decl("skiplist comparison function (cmp) is NULL"); \
|
||||
n_err++; \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (slist->max < 2) { \
|
||||
__skip_integrity_failure_##decl("skiplist max level must be 1 at minimum"); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (slist->level >= slist->max) { \
|
||||
/* level is 0-based, max of 12 means level cannot be > 11 */ \
|
||||
__skip_integrity_failure_##decl("skiplist level in header was >= max"); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (SKIPLIST_MAX_HEIGHT < 1) { \
|
||||
__skip_integrity_failure_##decl("SKIPLIST_MAX_HEIGHT cannot be less than 1"); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
if (SKIPLIST_MAX_HEIGHT > 0 && slist->max > SKIPLIST_MAX_HEIGHT) { \
|
||||
__skip_integrity_failure_##decl("slist->max cannot be greater than SKIPLIST_MAX_HEIGHT"); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
node = slist->slh_head; \
|
||||
for (nth = 0; nth < node->field.sle.height; nth++) { \
|
||||
if (node->field.sle.next[nth] == NULL || node->field.sle.next[nth] == slist->slh_tail) { \
|
||||
__skip_integrity_failure_##decl("the head's %u next node reference should not be NULL or pointing to the tail", nth); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
} \
|
||||
for (; nth < slist->max; nth++) { \
|
||||
if (node->field.sle.next[nth] != slist->slh_tail) { \
|
||||
__skip_integrity_failure_##decl("the head's %u next node reference above it's current height should always point to the tail", nth); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
if (slist->length > 0 && slist->slh_tail->field.sle.prev != slist->slh_head) { \
|
||||
__skip_integrity_failure_##decl("slist->length is 0, but tail->prev == head, not an internal node"); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
/* Validate the head node */ \
|
||||
\
|
||||
/* Validate the tail node */ \
|
||||
\
|
||||
/* Validate each node */ \
|
||||
nth = 0; \
|
||||
size = prefix##skip_size_##decl(slist); \
|
||||
node = prefix##skip_head_##decl(slist); \
|
||||
while (node) { \
|
||||
this = &node->field.sle; \
|
||||
if (this->next == NULL) { \
|
||||
__skip_integrity_failure_##decl("the %u node's [%p] next field should never NULL", nth, (void *)node); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
if (this->prev == NULL) { \
|
||||
__skip_integrity_failure_##decl("the %u node [%p] prev field should never NULL", nth, (void *)node); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
if (*this->next != node + (sizeof(struct __skiplist_##decl_idx) * slist->max)) { \
|
||||
__skip_integrity_failure_##decl("the %u node's [%p] next field isn't at the proper offset relative to the node", nth, (void *)node); \
|
||||
n_err++; \
|
||||
if (flags) \
|
||||
return n_err; \
|
||||
} \
|
||||
\
|
||||
node = prefix##skip_next_node_##decl(slist, node); \
|
||||
nth++; \
|
||||
} \
|
||||
\
|
||||
return 0; \
|
||||
}
|
||||
|
||||
#define SKIPLIST_KV_ACCESS(decl, prefix, ktype, vtype, qblk, rblk) \
|
||||
|
|
Loading…
Reference in a new issue