2024-03-21 16:30:37 +00:00
2024-03-30 02:43:46 +00:00
// OPTIONS to set before including sl.h
// ---------------------------------------------------------------------------
# define DEBUG
# define SKIPLIST_DIAGNOSTIC
/* Setting SKIPLIST_MAX_HEIGHT will do two things:
* 1 ) limit our max height across all instances of this data structure .
2024-03-20 18:33:08 +00:00
* 2 ) remove a heap allocation on frequently used paths , insert / remove / etc .
* so , use it when you need it .
*/
2024-03-30 02:43:46 +00:00
# define SKIPLIST_MAX_HEIGHT 12
2024-03-17 14:40:59 +00:00
2024-03-30 02:43:46 +00:00
// Include our monolithic ADT, the Skiplist!
// ---------------------------------------------------------------------------
# include "../include/sl.h"
2024-03-28 00:34:47 +00:00
2024-03-30 02:43:46 +00:00
// Local demo application OPTIONS:
// ---------------------------------------------------------------------------
# define VALIDATE
2024-03-30 13:30:03 +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-30 02:43:46 +00:00
// ---------------------------------------------------------------------------
# ifdef VALIDATE
# define CHECK __skip_integrity_check_sample(list, 0)
# else
# define CHECK ((void)0)
# endif
2024-03-17 14:40:59 +00:00
/*
* SKIPLIST EXAMPLE :
*
2024-03-30 02:43:46 +00:00
* This example creates a " sample " Skiplist where keys are integers , values are
* strings allocated on the heap .
*
* ' sample ' - meaning : 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-30 02:43:46 +00:00
struct sample_node {
2024-03-20 19:23:27 +00:00
int key ;
2024-03-21 16:30:37 +00:00
char * value ;
2024-03-30 20:19:48 +00:00
SKIPLIST_ENTRY ( sample ) 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-30 02:43:46 +00:00
sample , api_ , entries ,
/* compare entries: list, a, b, aux */
{
( void ) list ;
( void ) aux ;
if ( a - > key < b - > key )
return - 1 ;
if ( a - > key > b - > key )
return 1 ;
return 0 ;
} ,
/* free entry: node */
2024-03-30 19:34:42 +00:00
{ free ( node - > value ) ; } ,
2024-03-31 20:37:14 +00:00
/* update entry: rc, node, value */
2024-03-30 02:43:46 +00:00
{
2024-03-31 20:37:14 +00:00
if ( node - > value )
2024-03-30 02:57:41 +00:00
free ( node - > value ) ;
2024-03-31 20:37:14 +00:00
node - > value = ( char * ) value ;
2024-03-30 02:43:46 +00:00
} ,
/* archive an entry: rc, src, dest */
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
} ,
2024-03-30 02:43:46 +00:00
/* size in bytes of the content stored in an entry: bytes */
2024-03-30 19:34:42 +00:00
{ bytes = strlen ( node - > value ) + 1 ; } )
2024-03-19 13:41:36 +00:00
/*
2024-03-30 02:43:46 +00:00
* Skiplists are ordered , we need a way to compare entries .
2024-03-19 13:41:36 +00:00
* 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
2024-03-30 02:43:46 +00:00
__sample_key_compare ( sample_t * list , sample_node_t * a , sample_node_t * b , void * aux )
2024-03-19 01:19:26 +00:00
{
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-30 02:43:46 +00:00
*/
2024-03-17 14:40:59 +00:00
2024-03-30 02:43:46 +00:00
/*
* Optional : Getters and Setters
* - get , put , dup ( put ) , del , etc . functions
*
* 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_DECL_ACCESS (
sample , api_ , key , int , value , char * ,
/* query blk */ { query . key = key ; } ,
/* return blk */ { return node - > value ; } )
/*
* Optional : Snapshots
*
2024-03-31 00:36:27 +00:00
* Enable functions that enable returning to an earlier point in
* time when a snapshot was created .
2024-03-30 02:43:46 +00:00
*/
2024-03-30 22:00:06 +00:00
SKIPLIST_DECL_SNAPSHOTS ( sample , api_ , entries )
2024-03-30 02:43:46 +00:00
/*
* Optional : Archive to / from bytes
*
2024-03-31 00:36:27 +00:00
* Enable functions that can write / read the content of your Skiplist
* out / in to / from an array of bytes .
2024-03-30 02:43:46 +00:00
*/
SKIPLIST_DECL_ARCHIVE ( sample , api_ , entries )
/*
* Optional : As Hashtable
*
2024-03-31 00:36:27 +00:00
* Turn your Skiplist into a hash table .
2024-03-30 02:43:46 +00:00
*/
2024-03-31 00:36:27 +00:00
//TODO SKIPLIST_DECL_HASHTABLE(sample, api_, entries, snaps)
2024-03-30 02:43:46 +00:00
/*
* Optional : Check Skiplists at runtime
*
* Create a functions that validate the integrity of a Skiplist .
*/
SKIPLIST_DECL_VALIDATE ( sample , api_ , entries )
/* Optional: Visualize your Skiplist using DOT/Graphviz in PDF
*
* Create the functions used to annotate a visualization of a Skiplist .
*/
SKIPLIST_DECL_DOT ( sample , api_ , entries )
void
sprintf_sample_node ( sample_node_t * node , char * buf )
{
2024-03-31 20:41:05 +00:00
// sprintf(buf, "%d:%s (hits: %lu)", node->key, node->value, node->entries.sle_hits);
2024-03-30 02:43:46 +00:00
sprintf ( buf , " %d:%s " , node - > key , node - > value ) ;
}
// Function for this demo application.
// ---------------------------------------------------------------------------
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-30 02:43:46 +00:00
// ---------------------------------------------------------------------------
2024-03-19 01:19:26 +00:00
int
main ( )
{
2024-03-26 13:52:24 +00:00
int rc ;
2024-03-30 02:43:46 +00:00
# ifdef SNAPSHOTS
2024-03-29 18:44:01 +00:00
size_t snap_i = 0 ;
2024-03-30 22:00:06 +00:00
uint64_t snap_ids [ 2048 ] ;
2024-03-30 02:43:46 +00:00
# endif
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. */
2024-03-30 02:43:46 +00:00
sample_t * list = ( sample_t * ) malloc ( sizeof ( sample_t ) ) ;
2024-03-21 16:30:37 +00:00
if ( list = = NULL )
return ENOMEM ;
2024-03-31 00:36:27 +00:00
rc = api_skip_init_sample ( list , - 12 ) ;
2024-03-19 13:41:36 +00:00
if ( rc )
2024-03-20 19:23:27 +00:00
return rc ;
2024-03-30 13:30:03 +00:00
api_skip_snapshots_init_sample ( list ) ;
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-03-30 02:43:46 +00:00
api_skip_dot_sample ( of , list , gen + + , " init " , sprintf_sample_node ) ;
2024-03-24 13:32:47 +00:00
# endif
2024-03-30 02:43:46 +00:00
if ( api_skip_get_sample ( list , 0 ) ! = NULL )
2024-03-28 00:34:47 +00:00
perror ( " found a non-existent item! " ) ;
2024-03-30 02:43:46 +00:00
api_skip_del_sample ( list , 0 ) ;
CHECK ;
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-30 22:00:06 +00:00
snap_ids [ snap_i + + ] = api_skip_snapshot_sample ( 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 ] ;
2024-03-30 22:00:06 +00:00
memset ( msg , 0 , 1024 ) ;
2024-03-28 00:34:47 +00:00
# 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-31 20:37:14 +00:00
numeral = to_lower ( int_to_roman_numeral ( array [ i ] ) ) ;
rc = api_skip_put_sample ( list , array [ i ] , numeral ) ;
2024-03-30 02:43:46 +00:00
CHECK ;
2024-03-26 00:17:40 +00:00
# ifdef SNAPSHOTS
2024-03-29 23:48:11 +00:00
if ( i > TEST_ARRAY_SIZE + 1 ) {
2024-03-30 22:00:06 +00:00
snap_ids [ snap_i + + ] = api_skip_snapshot_sample ( list ) ;
2024-03-30 02:43:46 +00:00
CHECK ;
2024-03-29 23:48:11 +00:00
}
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 ) ;
2024-03-30 02:43:46 +00:00
api_skip_dot_sample ( of , list , gen + + , msg , sprintf_sample_node ) ;
CHECK ;
2024-03-24 13:32:47 +00:00
# endif
2024-03-30 02:43:46 +00:00
char * v = api_skip_get_sample ( list , array [ i ] ) ;
CHECK ;
2024-03-31 20:37:14 +00:00
char * upper_numeral = calloc ( 1 , strlen ( v ) + 1 ) ;
strncpy ( upper_numeral , v , strlen ( v ) ) ;
to_upper ( upper_numeral ) ;
api_skip_set_sample ( list , array [ i ] , upper_numeral ) ;
2024-03-30 02:43:46 +00:00
CHECK ;
2024-03-20 19:23:27 +00:00
}
2024-03-26 13:52:24 +00:00
numeral = int_to_roman_numeral ( - 1 ) ;
2024-03-30 02:43:46 +00:00
api_skip_dup_sample ( list , - 1 , numeral ) ;
CHECK ;
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 ) ;
2024-03-30 02:43:46 +00:00
api_skip_dot_sample ( of , list , gen + + , msg , sprintf_sample_node ) ;
CHECK ;
2024-03-24 13:32:47 +00:00
# endif
2024-03-26 13:52:24 +00:00
numeral = int_to_roman_numeral ( 1 ) ;
2024-03-30 02:43:46 +00:00
api_skip_dup_sample ( list , 1 , numeral ) ;
CHECK ;
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 ) ;
2024-03-30 02:43:46 +00:00
api_skip_dot_sample ( of , list , gen + + , msg , sprintf_sample_node ) ;
CHECK ;
2024-03-24 13:32:47 +00:00
# endif
2024-03-20 19:23:27 +00:00
2024-03-30 02:43:46 +00:00
api_skip_del_sample ( list , 0 ) ;
CHECK ;
if ( api_skip_get_sample ( list , 0 ) ! = NULL )
2024-03-28 00:34:47 +00:00
perror ( " found a deleted item! " ) ;
2024-03-30 02:43:46 +00:00
api_skip_del_sample ( list , 0 ) ;
CHECK ;
if ( api_skip_get_sample ( list , 0 ) ! = NULL )
2024-03-28 00:34:47 +00:00
perror ( " found a deleted item! " ) ;
2024-03-30 19:34:42 +00:00
int key = TEST_ARRAY_SIZE + 1 ;
api_skip_del_sample ( list , key ) ;
CHECK ;
key = - ( TEST_ARRAY_SIZE ) - 1 ;
numeral = int_to_roman_numeral ( key ) ;
api_skip_del_sample ( list , key ) ;
CHECK ;
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 ) ;
2024-03-30 02:43:46 +00:00
api_skip_dot_sample ( of , list , gen + + , msg , sprintf_sample_node ) ;
CHECK ;
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-31 00:36:27 +00:00
//api_skip_restore_snapshot_sample(list, snap_ids[snap_i - 1]);
2024-03-31 20:37:14 +00:00
//api_skip_release_snapshots_sample(list);
2024-03-26 00:17:40 +00:00
# endif
2024-03-21 20:35:18 +00:00
2024-03-30 02:43:46 +00:00
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_GTE , - ( TEST_ARRAY_SIZE ) - 1 ) - > value , int_to_roman_numeral ( - ( TEST_ARRAY_SIZE ) ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_GTE , - 2 ) - > value , int_to_roman_numeral ( - 2 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_GTE , 0 ) - > value , int_to_roman_numeral ( 1 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_GTE , 2 ) - > value , int_to_roman_numeral ( 2 ) ) = = 0 ) ;
assert ( api_skip_pos_sample ( list , SKIP_GTE , ( TEST_ARRAY_SIZE + 1 ) ) = = NULL ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_GT , - ( TEST_ARRAY_SIZE ) - 1 ) - > value , int_to_roman_numeral ( - ( TEST_ARRAY_SIZE ) ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_GT , - 2 ) - > value , int_to_roman_numeral ( - 1 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_GT , 0 ) - > value , int_to_roman_numeral ( 1 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_GT , 1 ) - > value , int_to_roman_numeral ( 2 ) ) = = 0 ) ;
assert ( api_skip_pos_sample ( list , SKIP_GT , TEST_ARRAY_SIZE ) = = NULL ) ;
assert ( api_skip_pos_sample ( list , SKIP_LT , - ( TEST_ARRAY_SIZE ) ) = = NULL ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_LT , - 1 ) - > value , int_to_roman_numeral ( - 2 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_LT , 0 ) - > value , int_to_roman_numeral ( - 1 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_LT , 2 ) - > value , int_to_roman_numeral ( 1 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_LT , ( TEST_ARRAY_SIZE + 1 ) ) - > value , int_to_roman_numeral ( TEST_ARRAY_SIZE ) ) = = 0 ) ;
assert ( api_skip_pos_sample ( list , SKIP_LTE , - ( TEST_ARRAY_SIZE ) - 1 ) = = NULL ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_LTE , - 2 ) - > value , int_to_roman_numeral ( - 2 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_LTE , 0 ) - > value , int_to_roman_numeral ( - 1 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_LTE , 2 ) - > value , int_to_roman_numeral ( 2 ) ) = = 0 ) ;
assert ( strcmp ( api_skip_pos_sample ( list , SKIP_LTE , ( TEST_ARRAY_SIZE + 1 ) ) - > value , int_to_roman_numeral ( TEST_ARRAY_SIZE ) ) = = 0 ) ;
api_skip_free_sample ( list ) ;
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-03-30 02:43:46 +00:00
api_skip_dot_end_sample ( of , gen ) ;
2024-03-22 03:09:16 +00:00
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
}