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