/* * test program for skip lists. * * Copyright 1996 by Gray Watson. * * $Id: skip_t.c,v 1.2 1998/03/25 01:07:03 gray Exp $ */ /* * Based on the Skip List algorithms described in the June 1990 issue * of CACM and invented by William Pugh in 1987. */ #include #include #include #define SKIP_T_MAIN #include "skip.h" #define TEST_PATH "skip_file" #define RANDOM_VALUE(x) ((random() % ((x) * 10)) / 10) #define SAMPLE_SIZE 1024 #define ITERATIONS 10000 #define MAX_ENTRIES 10240 static char *rcs_id = "$Id: skip_t.c,v 1.2 1998/03/25 01:07:03 gray Exp $"; #define MODE_CLEAR 0 /* then only a 1 in 100 */ #define MODE_INSERT 1 /* store a value into table */ #define MODE_REPLACE 2 /* store with overwrite */ #define MODE_RETRIEVE 3 /* retrieve value from table */ #define MODE_DELETE 4 /* delete value */ #define MODE_INFO 5 /* get info about current entry */ #define MODE_FIRST 6 /* return first entry in table */ #define MODE_NEXT 7 /* return next entry in table */ #define MODE_THIS 8 /* return current entry in table */ #define MODE_NORMALIZE 9 /* normalize the list */ #define MODE_MAX 10 typedef struct entry_st { char en_free; /* free flag */ long en_data; /* value to store */ struct entry_st *en_next_p; /* next pointer */ } entry_t; /* local vars */ static int call_c = 0; /* * compare two keys */ static int compare(const void *key1_p, const int key1_size, const void *key2_p, const int key2_size) { return *(int *)key1_p - *(int *)key2_p; } /* * dump the contents of LISTP */ static void dump_list(skip_t * list) { int *key_p, *data_p, ret, height, error; char marker[] = "*********************************************************"; for (ret = skip_first(list, (void **)&key_p, NULL, (void **)&data_p, NULL); ret == SKIP_ERROR_NONE; ret = skip_next(list, (void **)&key_p, NULL, (void **)&data_p, NULL)) { error = skip_info(list, &height); if (height < 0) { (void)printf("ERROR current-height: %s\n", skip_strerror(error)); break; } (void)printf("%15d %15d %2d %.*s\n", *key_p, *data_p, height, height, marker); } if (ret != SKIP_ERROR_NOT_FOUND) (void)printf("ERROR dump-list: %s\n", skip_strerror(ret)); } /* * run through a number ITER_N of transactions on SKIP_P. */ static void stress(skip_t *skip_p, const int iter_n, const char mmaping, const char verbose) { long data, *data_p, key, *key_p; int mode, iter_c, size, pnt_c, free_c, ret; entry_t *grid, *free_p, *grid_p, *last_p; char linear = 0; (void)printf("Stressing for %d iterations\n", iter_n); grid = malloc(sizeof(entry_t) * MAX_ENTRIES); if (grid == NULL) { (void)printf("problems allocating space for %d entries.\n", MAX_ENTRIES); exit(1); } /* initialize free list */ free_p = grid; for (grid_p = grid; grid_p < grid + MAX_ENTRIES; grid_p++) { grid_p->en_free = 1; grid_p->en_next_p = grid_p + 1; } /* redo the last next pointer */ (grid_p - 1)->en_next_p = NULL; free_c = MAX_ENTRIES; /* load the list */ if (mmaping) { } for (iter_c = 0; iter_c < iter_n;) { int which; /* decide what to do */ mode = RANDOM_VALUE(MODE_MAX); switch (mode) { case MODE_CLEAR: if (mmaping) continue; which = RANDOM_VALUE(300); if (which != 1) continue; call_c++; if (skip_clear(skip_p) != SKIP_ERROR_NONE) { (void)fprintf(stderr, "ERROR clearning list: %s\n", skip_strerror(ret)); continue; } if (verbose) (void)printf("skip cleared\n"); /* re-init free list */ free_p = grid; for (grid_p = grid; grid_p < grid + MAX_ENTRIES; grid_p++) { grid_p->en_free = 1; grid_p->en_next_p = grid_p + 1; } /* redo the last next pointer */ (grid_p - 1)->en_next_p = NULL; free_c = MAX_ENTRIES; linear = 0; iter_c++; if (verbose) (void)printf("skip cleared.\n"); break; case MODE_INSERT: if (mmaping) continue; if (free_c <= 0) continue; which = RANDOM_VALUE(free_c); last_p = NULL; grid_p = free_p; for (pnt_c = 0; pnt_c < which && grid_p != NULL; pnt_c++) { last_p = grid_p; grid_p = grid_p->en_next_p; } if (grid_p == NULL) { (void)printf("reached end of free list prematurely\n"); exit(1); } key = grid_p - grid; data = RANDOM_VALUE(1000000); call_c++; ret = skip_insert(skip_p, &key, sizeof(key), &data, sizeof(data), NULL, 0); if (ret == SKIP_ERROR_NONE) { if (verbose) (void)printf("stored in pos %ld, data %ld\n", key, data); grid_p->en_free = 0; grid_p->en_data = data; /* shift free list */ if (last_p == NULL) free_p = grid_p->en_next_p; else last_p->en_next_p = grid_p->en_next_p; grid_p->en_next_p = NULL; free_c--; iter_c++; } else (void)fprintf(stderr, "ERROR storing #%ld: %s\n", key, skip_strerror(ret)); break; case MODE_REPLACE: if (mmaping) continue; if (free_c >= MAX_ENTRIES) continue; key = RANDOM_VALUE(MAX_ENTRIES); if (grid[key].en_free) continue; data = RANDOM_VALUE(1000000); call_c++; ret = skip_insert(skip_p, &key, sizeof(key), &data, sizeof(data), NULL, 1); if (ret == SKIP_ERROR_NONE) { if (verbose) (void)printf("overwrite pos %ld with %ld\n", key, data); grid[key].en_free = 0; grid[key].en_data = data; grid[key].en_next_p = NULL; free_c--; iter_c++; } else (void)fprintf(stderr, "error overwriting #%ld: %s\n", key, skip_strerror(ret)); break; case MODE_RETRIEVE: if (free_c >= MAX_ENTRIES) continue; key = RANDOM_VALUE(MAX_ENTRIES); if (grid[key].en_free) continue; call_c++; ret = skip_retrieve(skip_p, &key, sizeof(key), (void **)&data_p, &size); if (ret == SKIP_ERROR_NONE) { if (sizeof(grid[key].en_data) == size && grid[key].en_data == *data_p) { if (verbose) (void)printf("retrieved key %ld, got data %ld\n", key, *data_p); } else (void)fprintf(stderr, "ERROR: retrieve key %ld: data %ld didn't match " "skip %ld\n", key, grid[key].en_data, *data_p); iter_c++; } else (void)fprintf(stderr, "error retrieving key %ld: %s\n", key, skip_strerror(ret)); break; case MODE_DELETE: if (mmaping) continue; if (free_c >= MAX_ENTRIES) continue; which = RANDOM_VALUE(5); if (which != 1) continue; key = RANDOM_VALUE(MAX_ENTRIES); if (grid[key].en_free) continue; call_c++; ret = skip_delete(skip_p, &key, sizeof(key), (void **)&data_p, &size); if (ret == SKIP_ERROR_NONE) { if (sizeof(grid[key].en_data) == size && grid[key].en_data == *data_p) { if (verbose) (void)printf("deleted key %ld, got data %ld\n", key, *data_p); } else (void)fprintf(stderr, "ERROR deleting key %ld: data %ld didn't match " "skip %ld\n", key, grid[key].en_data, *data_p); grid[key].en_free = 1; grid[key].en_next_p = free_p; free_p = grid + key; free_c++; if (free_c == MAX_ENTRIES) linear = 0; iter_c++; } else (void)fprintf(stderr, "error deleting key %ld: %s\n", key, skip_strerror(ret)); free(data_p); break; case MODE_INFO: { int height; call_c++; ret = skip_info(skip_p, &height); if (ret == SKIP_ERROR_NONE) { if (verbose) (void)printf("current entry has a height of %d\n", height); iter_c++; } else if (ret == SKIP_ERROR_LINEAR && (! linear)) { if (verbose) (void)printf("no first command run yet\n"); } else (void)fprintf(stderr, "ERROR: skip info: %s\n", skip_strerror(ret)); } break; case MODE_FIRST: call_c++; ret = skip_first(skip_p, (void **)&key_p, NULL, (void **)&data_p, NULL); if (ret == SKIP_ERROR_NONE) { linear = 1; if (verbose) (void)printf("first entry is key %ld, data %ld\n", *key_p, *data_p); iter_c++; } else if (free_c == MAX_ENTRIES && ret == SKIP_ERROR_NOT_FOUND) { if (verbose) (void)printf("no first in skip\n"); } else (void)fprintf(stderr, "ERROR: first in skip: %s\n", skip_strerror(ret)); break; case MODE_NEXT: call_c++; ret = skip_next(skip_p, (void **)&key_p, NULL, (void **)&data_p, NULL); if (ret == SKIP_ERROR_NONE) { if (verbose) (void)printf("next entry is key %ld, data %ld\n", *key_p, *data_p); iter_c++; } else if (ret == SKIP_ERROR_LINEAR && (! linear)) { if (verbose) (void)printf("no first command run yet\n"); } else if (ret == SKIP_ERROR_NOT_FOUND) { if (verbose) (void)printf("reached EOF with next in skip: %s\n", skip_strerror(ret)); linear = 0; } else { (void)fprintf(stderr, "ERROR: skip_next reports: %s\n", skip_strerror(ret)); linear = 0; } break; case MODE_THIS: call_c++; ret = skip_this(skip_p, (void **)&key_p, NULL, (void **)&data_p, NULL); if (ret == SKIP_ERROR_NONE) { if (verbose) (void)printf("this entry is key %ld, data %ld\n", *key_p, *data_p); iter_c++; } else if (ret == SKIP_ERROR_LINEAR && (! linear)) { if (verbose) (void)printf("no first command run yet\n"); } else { (void)fprintf(stderr, "ERROR: this skip: %s\n", skip_strerror(ret)); linear = 0; } break; case MODE_NORMALIZE: if (1) continue; if (mmaping) continue; which = RANDOM_VALUE(20); if (which != 1) continue; if (free_c >= MAX_ENTRIES) continue; call_c++; ret = skip_normalize(skip_p); if (ret == SKIP_ERROR_NONE) { if (verbose) (void)printf("list normalized\n"); iter_c++; } else { (void)fprintf(stderr, "ERROR: skip-normalize: %s\n", skip_strerror(ret)); } break; default: (void)printf("unknown mode %d\n", which); break; } } free(grid); } /* * test the i/o systems */ static void io_test(skip_t *skip_p, const char *path) { int error; skip_t *read_p; /* write it to disk */ error = skip_write(skip_p, path, 0600); if (error != SKIP_ERROR_NONE) { (void)printf("ERROR writing list to '%s': %s\n", path, skip_strerror(error)); exit(1); } /* read back from disk */ read_p = skip_read(path, compare, &error); if (read_p == NULL) { (void)printf("ERROR reading list from '%s': %s\n", path, skip_strerror(error)); exit(1); } /* now stress test the read list */ stress(read_p, 10000, 0, 0); error = skip_free(read_p); if (error != SKIP_ERROR_NONE) (void)printf("ERROR in skip free: %s\n", skip_strerror(error)); } /* * test the mmap systems */ static void mmap_test(skip_t *skip_p, const char *path) { int error; skip_t *mmap_p; mmap_p = skip_mmap(path, compare, &error); if (mmap_p == NULL) { (void)printf("ERROR reading list from '%s': %s\n", path, skip_strerror(error)); exit(1); } stress(mmap_p, 100, 1, 1); error = skip_munmap(mmap_p); if (error != SKIP_ERROR_NONE) { (void)printf("ERROR un-mapping list from '%s': %s\n", path, skip_strerror(error)); exit(1); } } int main(int argc, char ** argv) { skip_t *skip_p; int error; long seed; argc--, argv++; if (argc == 1) seed = atol(*argv); else seed = time(NULL); (void)srandom(seed); (void)printf("Seed for random is %ld\n", seed); skip_p = skip_alloc(compare, &error); if (skip_p == NULL) { (void)printf("ERROR allocating list: %s\n", skip_strerror(error)); exit(1); } #if 1 stress(skip_p, ITERATIONS, 0, 0); #else { long key = 1, data = 2; error = skip_insert(skip_p, &key, sizeof(key), &data, sizeof(data), NULL, 0); if (error != SKIP_ERROR_NONE) (void)printf("Could not insert into list: %s\n", skip_strerror(error)); } #endif dump_list(skip_p); skip_normalize(skip_p); (void)printf("\n\n"); (void)printf("Normalized:\n"); dump_list(skip_p); (void)printf("Testing I/O:\n"); io_test(skip_p, TEST_PATH); (void)printf("Testing Mmap:\n"); mmap_test(skip_p, TEST_PATH); error = skip_free(skip_p); if (error != SKIP_ERROR_NONE) (void)printf("ERROR in skip free: %s\n", skip_strerror(error)); (void)printf("Test program performed %d table calls\n", call_c); exit(0); }