diff --git a/examples/slm.c b/examples/slm.c index 7f6c4e0..7396006 100644 --- a/examples/slm.c +++ b/examples/slm.c @@ -50,7 +50,7 @@ SKIPLIST_DECL( new->key = node->key; char *nv = calloc(strlen(node->value) + 1, sizeof(char)); if (nv == NULL) - return NULL; /* leaks some memory... TODO */ + return NULL; // leaks some memory... TODO new->value = strncpy(nv, node->value, strlen(node->value)); }, /* size in bytes of the content stored in an entry by you */ @@ -66,13 +66,15 @@ sprintf_slex_node(slex_node_t *node, char *buf) } /* - * Getter + * Getters and Setters * It can be useful to have simple get/put-style API, but to * do that you'll have to supply some blocks of code used to * extract data from within your nodes. */ -SKIPLIST_GETTERS( - slex, api_, int, char *, { query.key = key; }, { return node->value; }) +SKIPLIST_KV_ACCESS( + slex, api_, int, char *, + /* query blk */ { query.key = key; }, + /* return blk */ { return node->value; }) /* * Now we need a way to compare the nodes you defined above. @@ -196,43 +198,32 @@ main() array[j] = i; shuffle(array, asz); - for (int i = 0; i <= asz; i++) { - struct slex_node *n; - char *v; - slex_node_t new; - rc = api_skip_alloc_node_slex(list, &n); - if (rc) - return rc; - n->key = array[i]; - n->value = to_lower(int_to_roman_numeral(array[i])); - api_skip_insert_slex(list, n); - v = api_skip_get_slex(list, array[i]); - ((void)v); - new.key = n->key; - new.value = to_upper(n->value); - api_skip_update_slex(list, &new); + for (int i = 0; i < asz; i++) { + rc = api_skip_put_slex(list, array[i], to_lower(int_to_roman_numeral(array[i]))); + char *v = api_skip_get_slex(list, array[i]); + api_skip_set_slex(list, array[i], to_upper(v)); } - slex_node_t q; - q.key = 0; - api_skip_remove_slex(list, &q); + api_skip_del_slex(list, 0); +#if 0 snap = api_skip_snapshot_slex(list); restored = api_skip_restore_snapshot_slex(snap, __skip_key_compare_slex); api_skip_dispose_snapshot_slex(snap); api_skip_destroy_slex(restored); +#endif - assert(api_skip_gte_slex(list, -6) == int_to_roman_numeral(-5)); - assert(api_skip_gte_slex(list, -2) == int_to_roman_numeral(-2)); - assert(api_skip_gte_slex(list, 0) == int_to_roman_numeral(0)); - assert(api_skip_gte_slex(list, 2) == int_to_roman_numeral(2)); - assert(api_skip_gte_slex(list, 6) == int_to_roman_numeral(0)); + assert(strcmp(api_skip_pos_slex(list, SKIP_GTE, -(TEST_ARRAY_SIZE) -1)->value, int_to_roman_numeral(-(TEST_ARRAY_SIZE))) == 0); + assert(strcmp(api_skip_pos_slex(list, SKIP_GTE, -2)->value, int_to_roman_numeral(-2)) == 0); + assert(strcmp(api_skip_pos_slex(list, SKIP_GTE, 0)->value, int_to_roman_numeral(1)) == 0); + assert(strcmp(api_skip_pos_slex(list, SKIP_GTE, 2)->value, int_to_roman_numeral(2)) == 0); + assert(api_skip_pos_slex(list, SKIP_GTE, (TEST_ARRAY_SIZE + 1)) == NULL); - assert(api_skip_lte_slex(list, -6) == int_to_roman_numeral(0)); - assert(api_skip_lte_slex(list, -2) == int_to_roman_numeral(-2)); - assert(api_skip_lte_slex(list, 0) == int_to_roman_numeral(-10)); - assert(api_skip_lte_slex(list, 2) == int_to_roman_numeral(2)); - assert(api_skip_lte_slex(list, 6) == int_to_roman_numeral(20)); + assert(api_skip_pos_slex(list, SKIP_LTE, -(TEST_ARRAY_SIZE) - 1) == NULL); + assert(strcmp(api_skip_pos_slex(list, SKIP_LTE, -2)->value, int_to_roman_numeral(-2)) == 0); + assert(strcmp(api_skip_pos_slex(list, SKIP_LTE, 0)->value, int_to_roman_numeral(-1)) == 0); + assert(strcmp(api_skip_pos_slex(list, SKIP_LTE, 2)->value, int_to_roman_numeral(2)) == 0); + assert(strcmp(api_skip_pos_slex(list, SKIP_LTE, (TEST_ARRAY_SIZE + 1))->value, int_to_roman_numeral(TEST_ARRAY_SIZE)) == 0); FILE *of = fopen("/tmp/slm.dot", "w"); if (!of) { diff --git a/include/sl.h b/include/sl.h index eb14b37..3e65856 100644 --- a/include/sl.h +++ b/include/sl.h @@ -201,6 +201,8 @@ struct decl##_node **nodes; \ }; \ \ + typedef enum { SKIP_EQ = 0, SKIP_LTE = -1, SKIP_LT = -2, SKIP_GTE = 1, SKIP_GT = 2 } skip_pos_##decl_t; \ + \ /* -- __skip_key_compare_ \ * \ * This function takes four arguments: \ @@ -423,12 +425,14 @@ return __skip_insert_##decl(slist, n, 1); \ } \ \ - /* -- skip_find_ \ - * Find a node that matches another node. This differs from the locate() \ + /* -- skip_position_eq_ \ + * Find a node that matches the node `n`. This differs from the locate() \ * API in that it does not return the path to the node, only the match. \ - * This avoids an alloc/free for the path. \ + * \ + * NOTE: This differs from _locate() in that it avoids an alloc/free \ + * for the path when SKIPLIST_MAX_HEIGHT == 1. \ */ \ - decl##_node_t *prefix##skip_find_##decl(decl##_t *slist, decl##_node_t *n) \ + decl##_node_t *prefix##skip_position_eq_##decl(decl##_t *slist, decl##_node_t *n) \ { \ unsigned int i; \ decl##_node_t *elm = slist->slh_head; \ @@ -449,10 +453,15 @@ return NULL; \ } \ \ - /* -- skip_find_gte \ - * Return the matching node or the next greater node after that. \ + /* -- skip_position_gte \ + * Position and return a cursor at the first node that is equal to \ + * or greater than the provided node `n`, otherwise if the largest \ + * key is less than the key in `n` return NULL. \ + * \ + * NOTE: This differs from _locate() in that it avoids an alloc/free \ + * for the path when SKIPLIST_MAX_HEIGHT == 1. \ */ \ - decl##_node_t *prefix##skip_find_gte_##decl(decl##_t *slist, decl##_node_t *n) \ + decl##_node_t *prefix##skip_position_gte_##decl(decl##_t *slist, decl##_node_t *n) \ { \ int cmp; \ unsigned int i; \ @@ -474,10 +483,45 @@ return elm; \ } \ \ - /* -- skip_find_lte \ - * Return the matching node or the last one before it. \ + /* -- skip_position_gt TODO \ + * Position and return a cursor at the first node that is greater than \ + * the provided node `n`. If the largestkey is less than the key in `n` \ + * return NULL. \ + * \ + * NOTE: This differs from _locate() in that it avoids an alloc/free \ + * for the path when SKIPLIST_MAX_HEIGHT == 1. \ */ \ - decl##_node_t *prefix##skip_find_lte_##decl(decl##_t *slist, decl##_node_t *n) \ + decl##_node_t *prefix##skip_position_gt_##decl(decl##_t *slist, decl##_node_t *n) \ + { \ + int cmp; \ + unsigned int i; \ + decl##_node_t *elm = slist->slh_head; \ + \ + if (slist == NULL || n == NULL) \ + return NULL; \ + \ + i = slist->slh_head->field.sle.len; \ + \ + do { \ + while (elm && __skip_key_compare_##decl(slist, elm->field.sle.next[i], n, slist->aux) < 0) \ + elm = elm->field.sle.next[i]; \ + } while (i--); \ + do { \ + elm = elm->field.sle.next[0]; \ + cmp = __skip_key_compare_##decl(slist, elm, n, slist->aux); \ + } while (cmp < 0); \ + return elm; \ + } \ + \ + /* -- skip_position_lte \ + * Position and return a cursor at the last node that is less than \ + * or equal to node `n`. \ + * Return NULL if nothing is less than or equal. \ + * \ + * NOTE: This differs from _locate() in that it avoids an alloc/free \ + * for the path when SKIPLIST_MAX_HEIGHT == 1. \ + */ \ + decl##_node_t *prefix##skip_position_lte_##decl(decl##_t *slist, decl##_node_t *n) \ { \ int cmp; \ unsigned int i; \ @@ -504,9 +548,74 @@ return elm; \ } \ \ + /* -- skip_position_lt TODO \ + * Position and return a cursor at the last node that is less than \ + * to the node `n`. Return NULL if nothing is less than or equal. \ + * \ + * NOTE: This differs from _locate() in that it avoids an alloc/free \ + * for the path when SKIPLIST_MAX_HEIGHT == 1. \ + */ \ + decl##_node_t *prefix##skip_position_lt_##decl(decl##_t *slist, decl##_node_t *n) \ + { \ + int cmp; \ + unsigned int i; \ + decl##_node_t *elm = slist->slh_head; \ + \ + if (slist == NULL || n == NULL) \ + return NULL; \ + \ + i = slist->slh_head->field.sle.len; \ + \ + do { \ + while (elm && __skip_key_compare_##decl(slist, elm->field.sle.next[i], n, slist->aux) < 0) \ + elm = elm->field.sle.next[i]; \ + } while (i--); \ + elm = elm->field.sle.next[0]; \ + if (__skip_key_compare_##decl(slist, elm, n, slist->aux) == 0) { \ + return elm; \ + } else { \ + do { \ + elm = elm->field.sle.prev; \ + cmp = __skip_key_compare_##decl(slist, elm, n, slist->aux); \ + } while (cmp > 0); \ + } \ + return elm; \ + } \ + \ + /* -- skip_position_ \ + * Position a cursor relative to `n`. \ + * This avoids an alloc/free for the path when SKIPLIST_MAX_HEIGHT == 1. \ + */ \ + decl##_node_t *prefix##skip_position_##decl(decl##_t *slist, skip_pos_##decl_t op, decl##_node_t *n) \ + { \ + decl##_node_t *node; \ + \ + switch (op) { \ + case (SKIP_LT): \ + node = prefix##skip_position_lt_##decl(slist, n); \ + break; \ + case (SKIP_LTE): \ + node = prefix##skip_position_lte_##decl(slist, n); \ + break; \ + case (SKIP_GTE): \ + node = prefix##skip_position_gte_##decl(slist, n); \ + break; \ + case (SKIP_GT): \ + node = prefix##skip_position_gt_##decl(slist, n); \ + break; \ + default: \ + case (SKIP_EQ): \ + node = prefix##skip_position_eq_##decl(slist, n); \ + break; \ + } \ + return node; \ + } \ + \ /* -- skip_update_ \ * Locates a node in the list that equals the `new` node and then \ * uses the `update_node_blk` to update the contents. \ + * \ + * \ * WARNING: Do not update the portion of the node used for ordering \ * (e.g. `key`) unless you really know what you're doing. \ */ \ @@ -517,7 +626,7 @@ if (slist == NULL || new == NULL) \ return -1; \ \ - node = prefix##skip_find_##decl(slist, new); \ + node = prefix##skip_position_eq_##decl(slist, new); \ if (node) { \ update_node_blk; \ return 0; \ @@ -615,7 +724,7 @@ return 0; \ } \ \ - /* -- skip_snapshot_ \ + /* -- skip_snapshot_ TODO/WIP \ * A snapshot is a read-only view of a Skiplist at a point in \ * time. Once taken, a snapshot must be restored or disposed. \ * Any number of snapshots can be created. \ @@ -653,7 +762,7 @@ return snap; \ } \ \ - /* -- skip_restore_snapshot_ */ \ + /* -- skip_restore_snapshot_ TODO/WIP */ \ decl##_t *prefix##skip_restore_snapshot_##decl(decl##_snap_t *snap, int (*cmp)(decl##_t * head, decl##_node_t * a, decl##_node_t * b, void *aux)) \ { \ int rc; \ @@ -705,7 +814,7 @@ return NULL; \ } \ \ - /* -- skip_dispose_snapshot_ */ \ + /* -- skip_dispose_snapshot_ TODO/WIP */ \ void prefix##skip_dispose_snapshot_##decl(decl##_snap_t *snap) \ { \ decl##_node_t *node; \ @@ -719,7 +828,7 @@ free(snap); \ } \ \ - /* -- skip_serialize_ \ + /* -- skip_serialize_ TODO/WIP \ * Similar to snapshot, but includes the values and encodes them \ * in a portable manner. \ */ \ @@ -760,7 +869,7 @@ return snap; \ } \ \ - /* -- skip_deserialize_snapshot_ */ \ + /* -- skip_deserialize_snapshot_ TODO/WIP */ \ decl##_t *prefix##skip_deserialize_##decl(decl##_snap_t *snap, int (*cmp)(decl##_t * head, decl##_node_t * a, decl##_node_t * b, void *aux)) \ { \ int rc; \ @@ -812,46 +921,65 @@ return NULL; \ } \ \ - /* -- __skip_integrity_check_ */ \ + /* -- __skip_integrity_check_ TODO */ \ static int __skip_integrity_check_##decl(decl##_t *slist) \ { \ - ((void)slist); /* TODO */ \ + ((void)slist); \ return 0; \ } -#define SKIPLIST_GETTERS(decl, prefix, ktype, vtype, qblk, rblk) \ - vtype prefix##skip_get_##decl(decl##_t *slist, ktype key) \ - { \ - decl##_node_t *node, query; \ - \ - qblk; \ - node = prefix##skip_find_##decl(slist, &query); \ - if (node) { \ - rblk; \ - } \ - return (vtype)0; \ - } \ - vtype prefix##skip_gte_##decl(decl##_t *slist, ktype key) \ - { \ - decl##_node_t *node, query; \ - \ - qblk; \ - node = prefix##skip_find_gte_##decl(slist, &query); \ - if (node != slist->slh_tail) { \ - rblk; \ - } \ - return (vtype)0; \ - } \ - vtype prefix##skip_lte_##decl(decl##_t *slist, ktype key) \ - { \ - decl##_node_t *node, query; \ - \ - qblk; \ - node = prefix##skip_find_lte_##decl(slist, &query); \ - if (node != slist->slh_head) { \ - rblk; \ - } \ - return (vtype)0; \ +#define SKIPLIST_KV_ACCESS(decl, prefix, ktype, vtype, qblk, rblk) \ + vtype prefix##skip_get_##decl(decl##_t *slist, ktype key) \ + { \ + decl##_node_t *node, query; \ + \ + qblk; \ + node = prefix##skip_position_eq_##decl(slist, &query); \ + if (node) { \ + rblk; \ + } \ + return (vtype)0; \ + } \ + \ + decl##_node_t *prefix##skip_pos_##decl(decl##_t *slist, skip_pos_##decl_t op, ktype key) \ + { \ + decl##_node_t *node, query; \ + \ + qblk; \ + node = prefix##skip_position_##decl(slist, op, &query); \ + if (node != slist->slh_head && node != slist->slh_tail) \ + return node; \ + return NULL; \ + } \ + \ + int prefix##skip_put_##decl(decl##_t *slist, ktype key, vtype value) \ + { \ + int rc; \ + decl##_node_t *node; \ + rc = prefix##skip_alloc_node_##decl(slist, &node); \ + if (rc) \ + return rc; \ + node->key = key; \ + node->value = value; \ + rc = prefix##skip_insert_##decl(slist, node); \ + if (rc) \ + prefix##skip_free_node_##decl(node); \ + return rc; \ + } \ + \ + int prefix##skip_set_##decl(decl##_t *slist, ktype key, vtype value) \ + { \ + decl##_node_t node; \ + node.key = key; \ + node.value = value; \ + return prefix##skip_update_##decl(slist, &node); \ + } \ + \ + int prefix##skip_del_##decl(decl##_t *slist, ktype key) \ + { \ + decl##_node_t node; \ + node.key = key; \ + return prefix##skip_remove_##decl(slist, &node); \ } #define SKIPLIST_DECL_DOT(decl, prefix, field) \