2024-03-21 16:30:37 +00:00
2024-03-19 18:24:33 +00:00
# include <assert.h>
2024-03-19 01:19:26 +00:00
# include <errno.h>
2024-03-18 18:58:35 +00:00
# include <stddef.h>
2024-03-17 14:40:59 +00:00
# include <stdint.h>
2024-03-19 01:19:26 +00:00
# include <stdio.h>
2024-03-22 19:03:06 +00:00
# include <stdarg.h>
2024-03-19 01:19:26 +00:00
# include <stdlib.h>
2024-03-21 16:30:37 +00:00
# include <string.h>
2024-03-20 16:18:46 +00:00
# include <time.h>
# include <unistd.h>
2024-03-19 01:19:26 +00:00
2024-03-22 19:03:06 +00:00
# define DEBUG 1
# define SKIPLIST_DEBUG slex
2024-03-28 02:41:26 +00:00
# define SKIPLIST_MAX_HEIGHT 12
2024-03-20 18:33:08 +00:00
/* 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 .
* so , use it when you need it .
*/
2024-03-17 14:40:59 +00:00
# include "../include/sl.h"
2024-03-28 00:34:47 +00:00
//define INTEGRITY
# ifdef INTEGRITY
# define INTEGRITY_CHK __skip_integrity_check_slex(list, 0)
# else
# define INTEGRITY_CHK ((void)0)
# endif
2024-03-28 23:41:28 +00:00
# define SNAPSHOTS
2024-03-28 02:41:26 +00:00
# define DOT
2024-03-28 01:37:38 +00:00
# define TEST_ARRAY_SIZE 10
2024-03-28 00:34:47 +00:00
2024-03-17 14:40:59 +00:00
/*
* SKIPLIST EXAMPLE :
*
* This example creates a Skiplist keys and values are integers .
2024-03-18 15:06:49 +00:00
* ' slex ' - meaning : SkipList EXample
2024-03-17 14:40:59 +00:00
*/
2024-03-18 18:58:35 +00:00
/*
* To start , you must create a type node that will contain the
* fields you ' d like to maintain in your Skiplist . In this case
2024-03-29 23:48:11 +00:00
* we map int - > char [ ] on the heap , but what you put here is up
* to you . You don ' t even need a " key " , just a way to compare one
* node against another , logic you ' ll provide in SKIP_DECL as a
* block below .
2024-03-18 18:58:35 +00:00
*/
2024-03-18 15:06:49 +00:00
struct slex_node {
2024-03-20 19:23:27 +00:00
int key ;
2024-03-21 16:30:37 +00:00
char * value ;
2024-03-21 13:31:32 +00:00
SKIPLIST_ENTRY ( slex_node ) entries ;
2024-03-17 14:40:59 +00:00
} ;
2024-03-18 18:58:35 +00:00
/*
* Generate all the access functions for our type of Skiplist .
2024-03-19 13:41:36 +00:00
*/
2024-03-19 16:33:11 +00:00
SKIPLIST_DECL (
2024-03-20 19:23:27 +00:00
slex , api_ , entries ,
2024-03-21 18:43:10 +00:00
/* free node */ { free ( node - > value ) ; } ,
2024-03-26 00:17:40 +00:00
/* update node */
{
//char *old = node->value;
2024-03-28 02:41:26 +00:00
dest - > value = src - > value ;
2024-03-26 00:17:40 +00:00
// In this case, don't free, we're just calling to_upper and using the same memory.
// free(old);
} ,
2024-03-23 13:07:38 +00:00
/* archive a node */
2024-03-21 16:30:37 +00:00
{
2024-03-28 02:41:26 +00:00
dest - > key = src - > key ;
char * nv = calloc ( strlen ( src - > value ) + 1 , sizeof ( char ) ) ;
2024-03-21 18:43:10 +00:00
if ( nv = = NULL )
2024-03-24 13:32:47 +00:00
rc = ENOMEM ;
else {
2024-03-28 02:41:26 +00:00
strncpy ( nv , src - > value , strlen ( src - > value ) ) ;
dest - > value = nv ;
2024-03-24 13:32:47 +00:00
}
2024-03-21 16:30:37 +00:00
} ,
/* size in bytes of the content stored in an entry by you */
2024-03-21 18:43:10 +00:00
{ size = strlen ( node - > value ) + 1 ; } )
2024-03-22 15:47:51 +00:00
/*
* Optional : Create a function that validates as much as possible the
* integrity of a Skiplist . This is called by the DOT function to
* ensure that it ' s possible to generate a graph .
*/
SKIPLIST_INTEGRITY_CHECK ( slex , api_ , entries )
2024-03-21 18:43:10 +00:00
/* Optional: Create the functions used to visualize a Skiplist (DOT/Graphviz) */
SKIPLIST_DECL_DOT ( slex , api_ , entries )
void
sprintf_slex_node ( slex_node_t * node , char * buf )
{
sprintf ( buf , " %d:%s " , node - > key , node - > value ) ;
}
2024-03-19 13:41:36 +00:00
/*
2024-03-21 20:35:18 +00:00
* Getters and Setters
2024-03-19 13:41:36 +00:00
* 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 .
*/
2024-03-21 20:35:18 +00:00
SKIPLIST_KV_ACCESS (
2024-03-24 01:07:57 +00:00
slex , api_ , key , int , value , char * ,
2024-03-21 20:35:18 +00:00
/* query blk */ { query . key = key ; } ,
/* return blk */ { return node - > value ; } )
2024-03-19 13:41:36 +00:00
/*
* Now we need a way to compare the nodes you defined above .
* Let ' s create a function with four arguments :
2024-03-18 18:58:35 +00:00
* - a reference to the Skiplist , ` slist `
* - the two nodes to compare , ` a ` and ` b `
* - and ` aux ` , which you can use to pass into this function
* any additional information required to compare objects .
* ` aux ` is passed from the value in the Skiplist , you can
* modify that value at any time to suit your needs .
*
2024-03-19 13:41:36 +00:00
* Your function should result in a return statement :
2024-03-18 18:58:35 +00:00
* a < b : return - 1
* a = = b : return 0
* a > b : return 1
*
* This result provides the ordering within the Skiplist . Sometimes
2024-03-19 13:41:36 +00:00
* your function will not be used when comparing nodes . This will
* happen when ` a ` or ` b ` are references to the head or tail of the
2024-03-18 18:58:35 +00:00
* list or when ` a = = b ` . In those cases the comparison function
* returns before using the code in your block , don ' t panic . : )
*/
2024-03-19 01:19:26 +00:00
int
__slm_key_compare ( slex_t * list , slex_node_t * a , slex_node_t * b , void * aux )
{
2024-03-20 19:23:27 +00:00
( void ) list ;
( void ) aux ;
if ( a - > key < b - > key )
return - 1 ;
if ( a - > key > b - > key )
return 1 ;
return 0 ;
2024-03-19 01:19:26 +00:00
}
2024-03-17 14:40:59 +00:00
2024-03-21 18:43:10 +00:00
static char *
to_lower ( char * str )
{
2024-03-21 16:30:37 +00:00
char * p = str ;
2024-03-21 18:43:10 +00:00
for ( ; * p ; + + p )
2024-03-26 13:52:24 +00:00
* p = ( char ) ( * p > = ' A ' & & * p < = ' Z ' ? * p | 0x60 : * p ) ;
2024-03-21 16:30:37 +00:00
return str ;
}
2024-03-21 18:43:10 +00:00
static char *
to_upper ( char * str )
{
2024-03-21 16:30:37 +00:00
char * p = str ;
2024-03-21 18:43:10 +00:00
for ( ; * p ; + + p )
2024-03-26 13:52:24 +00:00
* p = ( char ) ( * p > = ' a ' & & * p < = ' z ' ? * p & ~ 0x20 : * p ) ;
2024-03-21 16:30:37 +00:00
return str ;
}
2024-03-21 18:43:10 +00:00
static char *
int_to_roman_numeral ( int num )
{
int del [ ] = { 1000 , 900 , 500 , 400 , 100 , 90 , 50 , 40 , 10 , 9 , 5 , 4 , 1 } ; // Key value in Roman counting
char * sym [ ] = { " M " , " CM " , " D " , " CD " , " C " , " XC " , " L " , " XL " , " X " , " IX " , " V " , " IV " , " I " } ; // Symbols for key values
// The maximum length of the Roman numeral representation for the maximum signed 64-bit integer would be approximately 19 * 3 = 57 characters, assuming
// every digit is represented by its Roman numeral equivalent up to 3 repetitions. Therefore, 64 should be more than enough.
2024-03-28 01:37:38 +00:00
char * res = ( char * ) calloc ( 4096 , sizeof ( char ) ) ;
2024-03-21 16:30:37 +00:00
int i = 0 ;
if ( num < 0 ) {
res [ 0 ] = ' - ' ;
i + + ;
num = - num ;
}
if ( num = = 0 ) {
res [ 0 ] = ' 0 ' ;
return res ;
}
2024-03-21 18:43:10 +00:00
while ( num ) { // while input number is not zero
while ( num / del [ i ] ) { // while a number contains the largest key value possible
strcat ( res , sym [ i ] ) ; // append the symbol for this key value to res string
num - = del [ i ] ; // subtract the key value from number
2024-03-21 16:30:37 +00:00
}
2024-03-21 18:43:10 +00:00
i + + ; // proceed to the next key value
2024-03-21 16:30:37 +00:00
}
return res ;
}
2024-03-21 18:43:10 +00:00
void
shuffle ( int * array , size_t n )
{
2024-03-21 16:30:37 +00:00
if ( n > 1 ) {
size_t i ;
for ( i = n - 1 ; i > 0 ; i - - ) {
2024-03-21 18:43:10 +00:00
size_t j = ( unsigned int ) ( rand ( ) % ( i + 1 ) ) ; /* NOLINT(*-msc50-cpp) */
2024-03-21 16:30:37 +00:00
int t = array [ j ] ;
array [ j ] = array [ i ] ;
array [ i ] = t ;
}
}
}
2024-03-19 01:19:26 +00:00
int
main ( )
{
2024-03-26 13:52:24 +00:00
int rc ;
2024-03-29 18:44:01 +00:00
size_t snap_i = 0 ;
uint64_t snaps [ 2048 ] ;
2024-03-22 03:09:16 +00:00
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-03-26 00:17:40 +00:00
size_t gen = 0 ;
2024-03-22 03:09:16 +00:00
FILE * of = fopen ( " /tmp/slm.dot " , " w " ) ;
if ( ! of ) {
perror ( " Failed to open file /tmp/slm.dot " ) ;
return 1 ;
}
2024-03-24 13:32:47 +00:00
# endif
2024-03-21 16:30:37 +00:00
2024-03-20 19:23:27 +00:00
/* Allocate and initialize a Skiplist. */
slex_t * list = ( slex_t * ) malloc ( sizeof ( slex_t ) ) ;
2024-03-21 16:30:37 +00:00
if ( list = = NULL )
return ENOMEM ;
2024-03-28 00:34:47 +00:00
rc = api_skip_init_slex ( list , - 12 , __slm_key_compare ) ;
2024-03-19 13:41:36 +00:00
if ( rc )
2024-03-20 19:23:27 +00:00
return rc ;
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-03-26 13:52:24 +00:00
api_skip_dot_slex ( of , list , gen + + , " init " , sprintf_slex_node ) ;
2024-03-24 13:32:47 +00:00
# endif
2024-03-28 00:34:47 +00:00
if ( api_skip_get_slex ( list , 0 ) ! = NULL )
perror ( " found a non-existent item! " ) ;
api_skip_del_slex ( list , 0 ) ;
INTEGRITY_CHK ;
2024-03-20 19:23:27 +00:00
2024-03-26 00:17:40 +00:00
# ifdef SNAPSHOTS
2024-03-21 16:30:37 +00:00
/* Test creating a snapshot of an empty Skiplist */
2024-03-29 18:44:01 +00:00
snaps [ snap_i + + ] = api_skip_snapshot_slex ( list ) ;
2024-03-26 00:17:40 +00:00
# endif
2024-03-20 19:23:27 +00:00
/* Insert 7 key/value pairs into the list. */
2024-03-26 13:52:24 +00:00
int i , j ;
2024-03-28 00:34:47 +00:00
char * numeral ;
# ifdef DOT
char msg [ 1024 ] ;
# endif
2024-03-21 16:30:37 +00:00
int amt = TEST_ARRAY_SIZE , asz = ( amt * 2 ) + 1 ;
int array [ ( TEST_ARRAY_SIZE * 2 ) + 1 ] ;
2024-03-26 13:52:24 +00:00
for ( j = 0 , i = - amt ; i < = amt ; i + + , j + + )
2024-03-21 16:30:37 +00:00
array [ j ] = i ;
shuffle ( array , asz ) ;
2024-03-24 13:32:47 +00:00
for ( i = 0 ; i < asz ; i + + ) {
2024-03-26 13:52:24 +00:00
numeral = int_to_roman_numeral ( array [ i ] ) ;
rc = api_skip_put_slex ( list , array [ i ] , to_lower ( numeral ) ) ;
//rc = api_skip_put_slex(list, array[i], numeral);
2024-03-26 00:17:40 +00:00
INTEGRITY_CHK ;
# ifdef SNAPSHOTS
2024-03-29 23:48:11 +00:00
if ( i > TEST_ARRAY_SIZE + 1 ) {
snaps [ snap_i + + ] = api_skip_snapshot_slex ( list ) ;
INTEGRITY_CHK ;
}
2024-03-28 00:34:47 +00:00
# endif
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-03-26 13:52:24 +00:00
sprintf ( msg , " put key: %d value: %s " , i , numeral ) ;
api_skip_dot_slex ( of , list , gen + + , msg , sprintf_slex_node ) ;
2024-03-26 00:17:40 +00:00
INTEGRITY_CHK ;
2024-03-24 13:32:47 +00:00
# endif
2024-03-21 20:35:18 +00:00
char * v = api_skip_get_slex ( list , array [ i ] ) ;
2024-03-26 00:17:40 +00:00
INTEGRITY_CHK ;
2024-03-21 20:35:18 +00:00
api_skip_set_slex ( list , array [ i ] , to_upper ( v ) ) ;
2024-03-26 00:17:40 +00:00
INTEGRITY_CHK ;
2024-03-20 19:23:27 +00:00
}
2024-03-26 13:52:24 +00:00
numeral = int_to_roman_numeral ( - 1 ) ;
api_skip_dup_slex ( list , - 1 , numeral ) ;
2024-03-26 00:17:40 +00:00
INTEGRITY_CHK ;
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-03-26 13:52:24 +00:00
sprintf ( msg , " put dup key: %d value: %s " , i , numeral ) ;
api_skip_dot_slex ( of , list , gen + + , msg , sprintf_slex_node ) ;
2024-03-26 00:17:40 +00:00
INTEGRITY_CHK ;
2024-03-24 13:32:47 +00:00
# endif
2024-03-26 13:52:24 +00:00
numeral = int_to_roman_numeral ( 1 ) ;
api_skip_dup_slex ( list , 1 , numeral ) ;
2024-03-26 00:17:40 +00:00
INTEGRITY_CHK ;
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-03-26 13:52:24 +00:00
sprintf ( msg , " put dup key: %d value: %s " , i , numeral ) ;
api_skip_dot_slex ( of , list , gen + + , msg , sprintf_slex_node ) ;
2024-03-26 00:17:40 +00:00
INTEGRITY_CHK ;
2024-03-24 13:32:47 +00:00
# endif
2024-03-20 19:23:27 +00:00
2024-03-21 20:35:18 +00:00
api_skip_del_slex ( list , 0 ) ;
2024-03-26 00:17:40 +00:00
INTEGRITY_CHK ;
2024-03-28 00:34:47 +00:00
if ( api_skip_get_slex ( list , 0 ) ! = NULL )
perror ( " found a deleted item! " ) ;
api_skip_del_slex ( list , 0 ) ;
INTEGRITY_CHK ;
if ( api_skip_get_slex ( list , 0 ) ! = NULL )
perror ( " found a deleted item! " ) ;
int key = TEST_ARRAY_SIZE + 1 ;
numeral = int_to_roman_numeral ( key ) ;
api_skip_del_slex ( list , key ) ;
INTEGRITY_CHK ;
key = - ( TEST_ARRAY_SIZE ) - 1 ;
numeral = int_to_roman_numeral ( key ) ;
api_skip_del_slex ( list , key ) ;
INTEGRITY_CHK ;
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-03-26 13:52:24 +00:00
sprintf ( msg , " deleted key: %d, value: %s " , 0 , numeral ) ;
api_skip_dot_slex ( of , list , gen + + , msg , sprintf_slex_node ) ;
2024-03-26 00:17:40 +00:00
INTEGRITY_CHK ;
2024-03-24 13:32:47 +00:00
# endif
2024-03-20 19:23:27 +00:00
2024-03-26 00:17:40 +00:00
# ifdef SNAPSHOTS
2024-03-29 18:44:01 +00:00
slex_t * restored = api_skip_restore_snapshot_slex ( list , snaps [ snap_i - 1 ] ) ;
2024-03-28 02:41:26 +00:00
api_skip_release_snapshot_slex ( list ) ;
2024-03-29 17:03:37 +00:00
api_skip_free_slex ( restored ) ;
2024-03-26 00:17:40 +00:00
# endif
2024-03-21 20:35:18 +00:00
2024-03-21 21:34:34 +00:00
assert ( strcmp ( api_skip_pos_slex ( list , SKIP_GTE , - ( TEST_ARRAY_SIZE ) - 1 ) - > value , int_to_roman_numeral ( - ( TEST_ARRAY_SIZE ) ) ) = = 0 ) ;
2024-03-21 20:35:18 +00:00
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 ) ;
2024-03-21 21:34:34 +00:00
assert ( strcmp ( api_skip_pos_slex ( list , SKIP_GT , - ( TEST_ARRAY_SIZE ) - 1 ) - > value , int_to_roman_numeral ( - ( TEST_ARRAY_SIZE ) ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_slex ( list , SKIP_GT , - 2 ) - > value , int_to_roman_numeral ( - 1 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_slex ( list , SKIP_GT , 0 ) - > value , int_to_roman_numeral ( 1 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_slex ( list , SKIP_GT , 1 ) - > value , int_to_roman_numeral ( 2 ) ) = = 0 ) ;
assert ( api_skip_pos_slex ( list , SKIP_GT , TEST_ARRAY_SIZE ) = = NULL ) ;
assert ( api_skip_pos_slex ( list , SKIP_LT , - ( TEST_ARRAY_SIZE ) ) = = NULL ) ;
assert ( strcmp ( api_skip_pos_slex ( list , SKIP_LT , - 1 ) - > value , int_to_roman_numeral ( - 2 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_slex ( list , SKIP_LT , 0 ) - > value , int_to_roman_numeral ( - 1 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_slex ( list , SKIP_LT , 2 ) - > value , int_to_roman_numeral ( 1 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_slex ( list , SKIP_LT , ( TEST_ARRAY_SIZE + 1 ) ) - > value , int_to_roman_numeral ( TEST_ARRAY_SIZE ) ) = = 0 ) ;
assert ( api_skip_pos_slex ( list , SKIP_LTE , - ( TEST_ARRAY_SIZE ) - 1 ) = = NULL ) ;
2024-03-21 20:35:18 +00:00
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 ) ;
2024-03-20 19:23:27 +00:00
2024-03-29 17:03:37 +00:00
api_skip_free_slex ( list ) ;
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-03-22 03:09:16 +00:00
api_skip_dot_end_slex ( of , gen ) ;
fclose ( of ) ;
2024-03-24 13:32:47 +00:00
# endif
2024-03-20 19:23:27 +00:00
return rc ;
2024-03-17 14:40:59 +00:00
}