/** * @brief TEST_CASE - Sanity test of some key skiplist APIs using integers. */ static int simple( void ) { unsigned int i; skiplist_node_t *iter; skiplist_t *skiplist; skiplist = skiplist_create( SKIPLIST_PROPERTY_NONE, 5, int_compare, int_fprintf, NULL ); if( !skiplist ) return -1; if( skiplist_contains( skiplist, 10, NULL ) ) return -1; if( !skiplist_remove( skiplist, 10 ) ) return -1; for( i = 0; i < 10; ++i ) { if( skiplist_insert( skiplist, i ) ) return -1; if( !skiplist_contains( skiplist, i, NULL ) ) return -1; } for( i = 0; i < 100; ++i ) { unsigned int value = rand(); if( skiplist_insert( skiplist, value ) ) return -1; if( !skiplist_contains( skiplist, value, NULL ) ) return -1; } for( i = 5; i < 10; ++i ) if( skiplist_remove( skiplist, i ) ) return -1; for( iter = skiplist_begin( skiplist ); iter != skiplist_end(); iter = skiplist_next( iter ) ) { uintptr_t value = skiplist_node_value( iter, NULL ); if( value >= 5 && value < 10 ) return -1; } for( i = 0; i < skiplist_size( skiplist, NULL ); ++i ) skiplist_at_index( skiplist, i, NULL ); if( skiplist_fprintf_filename( "simple.dot", skiplist ) ) return -1; skiplist_destroy( skiplist ); return 0; }
/** * @brief TEST_CASE - Confirms that duplicate entries are disallowed when the skiplist is a set. */ static int duplicate_entries_disallowed( void ) { unsigned int i; skiplist_node_t *iter; skiplist_t *skiplist; skiplist = skiplist_create( SKIPLIST_PROPERTY_UNIQUE, 5, int_compare, int_fprintf, NULL ); if( !skiplist ) return -1; for( i = 0; i < 2; ++i ) { unsigned int j; for( j = 0; j < 5; ++j ) { if( skiplist_insert( skiplist, j ) ) return -1; if( skiplist_size( skiplist, NULL ) != (i ? 5 : j+1) ) return -1; } } for( i = 0; i < 5; ++i ) if( !skiplist_contains( skiplist, i, NULL ) ) return -1; for( i = 0, iter = skiplist_begin( skiplist ); iter != skiplist_end(); iter = skiplist_next( iter ), ++i ) if( skiplist_node_value( iter, NULL ) != i ) return -1; if( skiplist_fprintf_filename( "duplicate_entries_disallowed.dot", skiplist ) ) return -1; return 0; }
skiplist* skiplist_clone(skiplist* list, dict_key_datum_clone_func clone_func) { ASSERT(list != NULL); skiplist* clone = skiplist_new(list->cmp_func, list->del_func, list->max_link); if (clone) { skip_node* node = list->head->link[0]; while (node) { bool inserted = false; void** datum = skiplist_insert(clone, node->key, &inserted); if (!datum || !inserted) { skiplist_free(clone); return NULL; } *datum = node->datum; node = node->link[0]; } if (clone_func) { node = clone->head->link[0]; while (node) { clone_func(&node->key, &node->datum); node = node->link[0]; } } } return clone; }
int main() { int arr[] = {3, 6, 9, 2, 11, 1, 4}, i; skiplist list; skiplist_init(&list); printf("Insert:--------------------\n"); for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++) { skiplist_insert(&list, arr[i], arr[i]); } skiplist_dump(&list); printf("Search:--------------------\n"); int keys[] = {3, 4, 7, 10, 111}; for (i = 0; i < sizeof(keys)/sizeof(keys[0]); i++) { snode *x = skiplist_search(&list, keys[i]); if (x) { printf("key = %d, value = %d\n", keys[i], x->value); } else { printf("key = %d, not fuound\n", keys[i]); } } printf("Search:--------------------\n"); skiplist_delete(&list, 3); skiplist_delete(&list, 9); skiplist_dump(&list); return 0; }
/* skiplist_insert_copy SKIPLIST KEY KLEN VALUE VLEN * Make a copy of VLEN bytes of VALUE data in SKIPLIST's private pool, and * insert a pointer to the copy under the given KLEN-byte KEY. */ int skiplist_insert_copy(skiplist S, const void *key, const size_t klen, const void *val, const size_t vlen) { void *v; v = pool_malloc(S->P, vlen); memcpy(v, val, vlen); if (!skiplist_insert(S, key, klen, v)) { pool_free(S->P, v); /* XXX */ return 0; } else return 1; }
int main(int argc, char **argv) { skiplist *list = skiplist_new(compare_word); assert(list != NULL); FILE *f = fopen("../_test/wordlist.txt", "r"); assert(f != NULL); char *key; int i = 0; long long start, stop; start = get_ustime_sec(); while (!feof(f)) { i++; key = (char *)malloc(sizeof(char) * 30); assert(key != NULL); fscanf(f, "%s\n", key); skiplist_insert(list, key, &value); } stop = get_ustime_sec(); fclose(f); printf("put %d word in skiplist cost time %lldμs\n", i, stop-start); /* 查询 */ char test[30] = "comically"; int *t = skiplist_search(list, test); if (t) { printf("%s in wordlist and value = %d\n", test, *t); } else { printf("%s not in wordlist\n", test); } skiplist_delete(list, test); t = skiplist_search(list, test); if (t) { printf("%s in wordlist\n", test); } else { printf("%s not in wordlist\n", test); } /** * 在删除 Hashmap 前,需要自己管理 Key, Value 指向的内存 * 这里并没有释放 Key 内存 */ skiplist_free(list); return 0; }
int main() { struct skiplist *s; struct node *p; int i,level; s = skiplist_create(); for(i=0; i<20; i++) { skiplist_insert(s,rand()%40); } print(s); return 0; }
int main(int argc, char **argv) { ut_init(basename(argv[0])); ut_testing("skiplist_create(6, 50, libhl_cmp_keys_int32, free)"); skiplist_t *skl = skiplist_create(6, 50, libhl_cmp_keys_int32, free); if (skl) ut_success(); else ut_failure("Can't create a new binomial heap"); ut_testing("skiplist_insert(0..99)"); int i; for (i = 0; i < 100; i++) { char *val = malloc(4); snprintf(val, 4, "%d", i); skiplist_insert(skl, &i, sizeof(i), val); } ut_validate_int(skiplist_count(skl), 100); int test_key = 50; ut_testing("skiplist_search(50) = \"50\""); char *val = skiplist_search(skl, &test_key, sizeof(int)); ut_validate_string(val, "50"); ut_testing("skiplist_remove(50, &old_value)"); val = NULL; int rc = skiplist_remove(skl, &test_key, sizeof(int), (void **)&val); ut_validate_int(rc, 0); ut_testing("old_value is \"50\""); ut_validate_string(val, "50"); free(val); ut_testing("skiplist_search(50) = NULL"); val = skiplist_search(skl, &test_key, sizeof(int)); ut_validate_string(val, NULL); skiplist_destroy(skl); ut_summary(); return ut_failed; }
struct skiplist *_read_mmap(struct sst *sst, size_t count) { int i; int fd; int result; int fcount; int blk_sizes; char file[FILE_PATH_SIZE]; struct sst_block *blks; struct skiplist *merge = NULL; struct footer footer; int fsize = sizeof(struct footer); memset(file, 0, FILE_PATH_SIZE); snprintf(file, FILE_PATH_SIZE, "%s/%s", sst->basedir, sst->name); fd = open(file, O_RDWR, 0644); result = lseek(fd, -fsize, SEEK_END); if(result == -1) abort(); result = read(fd, &footer, fsize); if(result == -1) abort(); fcount = from_be32(footer.count); blk_sizes = fcount * sizeof(struct sst_block); blks = mmap(0, blk_sizes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(blks == MAP_FAILED) { __DEBUG(LEVEL_ERROR, "%s", "Error: sst_bloom un-mmapping the file"); goto out; } merge = skiplist_new(fcount + count + 1); for(i = 0; i < fcount; i++) { skiplist_insert(merge, blks[i].key, from_be64(blks[i].offset), ADD); } if(munmap(blks, blk_sizes) == -1) { __DEBUG(LEVEL_ERROR, "%s", "read_map:un-mmapping the file"); } out: close(fd); return merge; }
/** * @brief TEST_CASE - Confirms incorrect inputs are handled gracefully for skiplist_next. */ static int abuse_skiplist_next( void ) { skiplist_t *skiplist; skiplist = skiplist_create( SKIPLIST_PROPERTY_NONE, 5, int_compare, int_fprintf, NULL ); if( !skiplist ) return -1; if( skiplist_insert( skiplist, 1 ) ) return -1; if( skiplist_next( NULL ) ) return -1; skiplist_destroy( skiplist ); return 0; }
int main(int argc, char *argv[]) { srand(clock()); SkipList_t *ptrSl = skiplist_alloc(10,1); int64_t i; for (i = 1; i <= 10; i++) { skiplist_insert(ptrSl, i, (void*)(i*i)); SlKey_t slKey; int64_t cValue; if (skiplist_index(ptrSl, (i+1)/2-1, &slKey, (void**)(&cValue))) { assert(slKey == (i+1)/2); } else { assert(0); } } for (i = 0; i < 10; i++) { SlKey_t slKey; int64_t cValue; if (skiplist_index(ptrSl, i, &slKey, (void**)(&cValue))) { assert(slKey == i+1); } else { assert(0); } } int32_t ixRm = 5; assert(skiplist_delete(ptrSl, ixRm)); for (i = 1; i <= 10; i++) { int64_t cValue = (int64_t)skiplist_find(ptrSl, i); if (i != ixRm) { assert(cValue == i*i); } else { assert(cValue == 0); } } for (i = 1; i <= 10; i+=2) { int32_t fSuccess = skiplist_delete(ptrSl,i); if (i != ixRm) { assert(fSuccess); } else { assert(!fSuccess); } } skiplist_free(ptrSl); return 1; }
int index_add(struct index *idx, struct slice *sk, struct slice *sv) { uint64_t value_offset; struct skiplist *list; struct skiplist *new_list; value_offset = log_append(idx->log, sk, sv); list = idx->list; if(!list) { __DEBUG(LEVEL_ERROR, "ERROR: List<%d> is NULL", idx->lsn); return 0; } if(!skiplist_notfull(list)) { idx->bg_merge_count++; /* If the detached-merge thread isnot finished, hold on it * Notice: it will block the current process, * but it happens only once in a thousand years on production environment. */ pthread_mutex_lock(&idx->merge_mutex); /* start to merge with detached thread */ pthread_t tid; idx->park->list = list; idx->park->lsn = idx->lsn; pthread_mutex_unlock(&idx->merge_mutex); pthread_create(&tid, &idx->attr, _merge_job, idx); idx->mtbl_rem_count = 0; new_list = skiplist_new(idx->max_mtbl_size); idx->list = new_list; idx->lsn++; log_next(idx->log, idx->lsn); } skiplist_insert(idx->list, sk->data, value_offset, sv == NULL ? DEL : ADD); idx->mtbl_rem_count++; return 1; }
static int test_insert() { int i; int result; int64_t start_time; int64_t end_time; void *value; instance_count = 0; start_time = get_current_time_ms(); for (i=0; i<COUNT; i++) { if ((result=skiplist_insert(&sl, numbers + i)) != 0) { return result; } instance_count++; } assert(instance_count == COUNT); end_time = get_current_time_ms(); printf("insert time used: %"PRId64" ms\n", end_time - start_time); start_time = get_current_time_ms(); for (i=0; i<COUNT; i++) { value = skiplist_find(&sl, numbers + i); assert(value != NULL && *((int *)value) == numbers[i]); } end_time = get_current_time_ms(); printf("find time used: %"PRId64" ms\n", end_time - start_time); start_time = get_current_time_ms(); i = 0; skiplist_iterator(&sl, &iterator); while ((value=skiplist_next(&iterator)) != NULL) { i++; assert(i == *((int *)value)); } assert(i==COUNT); end_time = get_current_time_ms(); printf("iterator time used: %"PRId64" ms\n", end_time - start_time); return 0; }
int main(int argc, char *argv[]) { int i, n, seed, key; SkipList skiplist; Cell *node; if (argc < 2 || argc > 3) { printf("Usage: %s <n> [seed]\n", argv[0]); exit(1); } n = atoi(argv[1]); if (argc == 3) seed = atoi(argv[2]); else seed = time(NULL); srand(seed); skiplist_init(&skiplist); printf("[+] dump key: \n"); printf(" (i , key)\n"); for (i = 0; i < n; i++) { key = rand() % SKIPLIST_MAX_KEY; printf(" (%-4d , %4d)\n", i, key); skiplist_insert(&skiplist, key); skiplist_vertical_print(&skiplist); } skiplist_vertical_print(&skiplist); /*node = skiplist_search(&skiplist, 15);*/ if (NULL) printf("[+] key %d found.\n", node->key); else printf("[-] Key 15 not found.\n"); return 0; }
int main(int argc, char** argv) { if (argc != 2) { printf("Usage: %s HPROF_FILE\n", argv[0]); exit(1); } FILE* hprof_fp = fopen(argv[1], "r"); die(hprof_fp == NULL, "File error"); hprof_header* header; char version[19]; u4 ident_size; int count = fread(version, sizeof(version), 1, hprof_fp); die(count != 1, "Unable to read hprof header"); count = fread(&ident_size, sizeof(u4), 1, hprof_fp); die(count != 1, "Unable to read hprof header"); int success = fseek(hprof_fp, sizeof(u8), SEEK_CUR); die(success != 0, "Could not seek forward in the file"); printf("Header: version=%s identsize=%ju\n", version, (uintmax_t) be32_to_native(ident_size)); // For now just reuse it hprof_record_header* record_header = calloc(1, sizeof(hprof_record_header)); hprof_heap_summary* heap_summary = calloc(1, sizeof(hprof_heap_summary)); // The string table is a skip list that should give us // O(n log n) insertion and O(log n) lookup, This is not as // good as a malloc'd array, but does not suffer from the // out of order issues that the string id's present us with skiplist_t *string_table = malloc(sizeof(skiplist_t)); skiplist_init(string_table); skiplist_set_cmp_fn(string_table, stringtable_cmp); skiplist_set_search_fn(string_table, stringtable_search); skiplist_set_destroy_fn(string_table, __destroy); skiplist_t *class_table = malloc(sizeof(skiplist_t)); skiplist_init(class_table); skiplist_set_cmp_fn(class_table, classtable_cmp); skiplist_set_search_fn(class_table, classtable_search); skiplist_set_destroy_fn(class_table, __destroy); while(1) { if (feof(hprof_fp)) { break; } //read_record_header(record_header, hprof_fp); funpack(hprof_fp, "bd>d>", &record_header->tag, &record_header->profiling_ms, &record_header->remaining_bytes); //printf("Tag=%ju\n", (uintmax_t) record_header->tag); switch(record_header->tag) { case UTF8: //printf("UTF8\n"); ; hprof_utf8 *utf8 = malloc(sizeof(hprof_utf8)); funpack(hprof_fp, "q>", &utf8->id); //fread(&utf8->id, sizeof(id), 1, hprof_fp); long bytes_remain = record_header->remaining_bytes - sizeof(id); // Awesome, hprof strings are utf8, this is good ! the only established // C type is char and its a byte wide :D utf8->num_chars = bytes_remain / sizeof(char); utf8->hprof_offset = ftell(hprof_fp); skiplist_insert(string_table, utf8); fseek(hprof_fp, bytes_remain, SEEK_CUR); break; case LOAD_CLASS: //printf("LOAD_CLASS\n"); ; hprof_load_class *class = malloc(sizeof(hprof_load_class)); fread(&class->serial_number, sizeof(u4), 1, hprof_fp); class->serial_number = be32_to_native(class->serial_number); fseek(hprof_fp, sizeof(id) + sizeof(u4), SEEK_CUR); fread(&class->name_id, sizeof(id), 1, hprof_fp); class->name_id = be64_to_native(class->name_id); skiplist_insert(class_table, class); //fseek(hprof_fp, be32_to_native(record_header->remaining_bytes), SEEK_CUR); break; case UNLOAD_CLASS: //printf("UNLOAD_CLASS\n"); fseek(hprof_fp, record_header->remaining_bytes, SEEK_CUR); break; case FRAME: //printf("FRAME\n"); // This is bad !!! If it is slow think about doing two complete passes to // make all the I/O as sequential as possible // This implementation is going to jump all over the hprof file ; long curr_pos = ftell(hprof_fp); id ids[4]; fread(&ids, sizeof(id), 4, hprof_fp); u4 class_serial_num; fread(&class_serial_num, sizeof(u4), 1, hprof_fp); class_serial_num = be32_to_native(class_serial_num); hprof_load_class *clazz = (hprof_load_class*) skiplist_search(class_table, &class_serial_num); i4 line_num; fread(&line_num, sizeof(i4), 1, hprof_fp); line_num = be32_to_native(line_num); char *method_name = read_string(be64_to_native(ids[1]), string_table, hprof_fp); char *method_sig = read_string(be64_to_native(ids[2]), string_table, hprof_fp); char *source_file = read_string(be64_to_native(ids[3]), string_table, hprof_fp); char *class_name = "<UNKNOWN_CLASS>"; if(clazz != NULL) { class_name = read_string(clazz->name_id, string_table, hprof_fp); } // SOMETIMES NULL //printf("%s\n", read_string(ids[3], string_table, hprof_fp)); class_name = (class_name == NULL) ? "<UNKNOWN_CLASS>" : class_name; method_name = (method_name == NULL) ? "<UNKNOWN_METHOD>" : method_name; method_sig = (method_sig == NULL) ? "<UNKNOWN_METHOD_SIG>" : method_sig; source_file = (source_file == NULL) ? "<UNKNOWN_SOURCE>" : source_file; switch(line_num) { case -1: printf("Class %s\n \tMethod=%s\n\tSource=%s @ <UNKNOWN>\n", class_name, method_name, source_file); break; case -2: printf("Class %s\n \tMethod=%s\n\tSource=%s @ <COMPILED_METHOD>\n", class_name, method_name, source_file); break; case -3: printf("Class %s\n \tMethod=%s\n\tSource=%s @ <NATIVE_METHOD>\n", class_name, method_name, source_file); break; default: printf("Class %s\n \tMethod=%s\n\tSource=%s @ %ji\n", class_name, method_name, source_file, (intmax_t) line_num); break; } break; case TRACE: //printf("TRACE\n"); ; hprof_stacktrace* stacktrace = calloc(1, sizeof(hprof_stacktrace)); fread(&stacktrace->serial_number, sizeof(u4), 1, hprof_fp); fread(&stacktrace->thread_number, sizeof(u4), 1, hprof_fp); fread(&stacktrace->number_frames, sizeof(u4), 1, hprof_fp); printf("StackTrace serial_num=%ju " "thread_num=%ju num_frames=%ju\n", (uintmax_t) be64_to_native(stacktrace->serial_number), (uintmax_t) be64_to_native(stacktrace->thread_number), (uintmax_t) be64_to_native(stacktrace->number_frames)); for (int i=0; i < be32_to_native(stacktrace->number_frames); i++) { id frame_id; fread(&frame_id, sizeof(id), 1, hprof_fp); printf("FrameID=%ju\n", (uintmax_t) be64_to_native(frame_id)); } free(stacktrace); break; case ALLOC_SITES: //printf("ALLOC_SITES\n"); fseek(hprof_fp, be32_to_native(record_header->remaining_bytes), SEEK_CUR); break; case HEAP_SUMMARY: //printf("HEAP SUMMARY\n"); fread(&heap_summary->live_bytes, sizeof(u4), 1, hprof_fp); fread(&heap_summary->live_instances, sizeof(u4), 1, hprof_fp); fread(&heap_summary->allocated_bytes, sizeof(u8), 1, hprof_fp); fread(&heap_summary->allocated_instances, sizeof(u8), 1, hprof_fp); printf("Heap summary:\n\tReachable bytes=%ju\n\t" "Reachable instances=%ju\n\t" "Allocated bytes=%ju\n\t" "Allocated instances=%ju\n", (uintmax_t) be32_to_native(heap_summary->live_bytes), (uintmax_t) be32_to_native(heap_summary->live_instances), (uintmax_t) be64_to_native(heap_summary->allocated_bytes), (uintmax_t) be64_to_native(heap_summary->allocated_instances)); break; case START_THREAD: //printf("START_THREAD\n"); fseek(hprof_fp, be32_to_native(record_header->remaining_bytes), SEEK_CUR); break; case END_THREAD: //printf("END_THREAD\n"); fseek(hprof_fp, be32_to_native(record_header->remaining_bytes), SEEK_CUR); break; case HEAP_DUMP: //printf("HEAP_DUMP\n"); fseek(hprof_fp, be32_to_native(record_header->remaining_bytes), SEEK_CUR); break; case CPU_SAMPLES: //printf("CPU_SAMPLES\n"); fseek(hprof_fp, be32_to_native(record_header->remaining_bytes), SEEK_CUR); break; case CONTROL_SETTINGS: //printf("CONTROL_SETTINGS\n"); fseek(hprof_fp, be32_to_native(record_header->remaining_bytes), SEEK_CUR); break; case HEAP_DUMP_SEGMENT: //printf("HEAP_DUMP_SEGMENT\n"); fseek(hprof_fp, be32_to_native(record_header->remaining_bytes), SEEK_CUR); break; case HEAP_DUMP_END: //printf("HEAP_DUMP_END\n"); fseek(hprof_fp, be32_to_native(record_header->remaining_bytes), SEEK_CUR); exit(1); break; default: printf("BANG\n"); exit(1); break; } //printf("%lu\n", ftell(hprof_fp)); } skiplist_destroy(string_table); free(string_table); skiplist_destroy(class_table); free(class_table); fclose(hprof_fp); return 0; }
/** * @brief TEST_CASE - Measures insertion trade off between number of elements in the list and number of links per node. */ static int link_trade_off_insert( void ) { #define MAX_LINKS (SKIPLIST_MAX_LINKS) #define INSERTIONS_LOG2 (16) struct timespec stamps[INSERTIONS_LOG2 + 2]; FILE *fp; unsigned int links; unsigned int i; const char *seperator; fp = fopen( "link_trade_off_insert.gplot", "w" ); if( !fp ) return -1; fprintf(fp, "set term qt\n"); fprintf(fp, "set key off\n"); fprintf(fp, "set logscale\n"); fprintf(fp, "set grid xtics ytics mxtics mytics\n"); fprintf(fp, "set style textbox opaque noborder\n"); fprintf(fp, "set title \"Average Insertion Time for Skiplists with Varying Link Counts\"\n"); fprintf(fp, "set xlabel \"Number of Elements in the Skiplist\"\n"); fprintf(fp, "set ylabel \"Average Time for One Insertion (ns)\"\n"); fprintf(fp, "plot " ); seperator = ""; for( i = 0; i < MAX_LINKS; ++i ) { fprintf(fp, "%s\"link_trade_off_insert_%u.dat\" using 1:2 with lines lt -1," "\"\" using 1:2:($0*0+%u) with labels center boxed notitle", seperator, i + 1, i + 1); seperator = ",\\\n\t"; } fprintf(fp, "\n"); fprintf(fp, "pause -1\n"); fclose( fp ); for( links = MAX_LINKS; links > 0; --links ) { skiplist_t *skiplist; char filename[64]; unsigned int next; sprintf( filename, "link_trade_off_insert_%u.dat", links ); fp = fopen( filename, "w" ); if( !fp ) return -1; skiplist = skiplist_create( SKIPLIST_PROPERTY_NONE, links, int_compare, int_fprintf, NULL ); if( !skiplist ) return -1; next = 0; for( i = 0; i < (1 << INSERTIONS_LOG2); ++i ) { /* Sample at powers of 2. */ if( (i & (i - 1)) == 0 ) { time_stamp( &stamps[next] ); /* Stop trying if it's taking too long. */ if( next && time_diff_ns( &stamps[next - 1], &stamps[next] ) > 300000000LLU ) break; ++next; } if( skiplist_insert( skiplist, rand() ) ) return -1; } time_stamp( &stamps[next] ); ++next; skiplist_destroy( skiplist ); for( i = 1; i < next; ++i ) { const unsigned int node_count = 1 << (i - 1); fprintf(fp, "%u\t%f\n", node_count, time_diff_ns( &stamps[0], &stamps[i] ) / (double)node_count ); } fclose( fp ); } #undef MAX_LINKS #undef INSERTIONS_LOG2 return 0; }
/** * @brief TEST_CASE - Measures lookup trade off between number of elements in the list and number of links per node. */ static int link_trade_off_lookup( void ) { #define MAX_LINKS (SKIPLIST_MAX_LINKS) #define INSERTIONS_LOG2 (16) unsigned int i; FILE *fp; const char *seperator; fp = fopen( "link_trade_off_lookup.gplot", "w" ); if( !fp ) return -1; fprintf(fp, "set term qt\n"); fprintf(fp, "set key off\n"); fprintf(fp, "set logscale\n"); fprintf(fp, "set grid xtics ytics mxtics mytics\n"); fprintf(fp, "set style textbox opaque noborder\n"); fprintf(fp, "set title \"Average Lookup Time for Skiplists with Varying Link Counts\"\n"); fprintf(fp, "set xlabel \"Number of Elements in the Skiplist\"\n"); fprintf(fp, "set ylabel \"Average Time for One Lookup (ns)\"\n"); fprintf(fp, "plot " ); seperator = ""; for( i = 0; i < MAX_LINKS; ++i ) { fprintf(fp, "%s\"link_trade_off_lookup.dat\" using 1:%u with lines lt -1," "\"\" using 1:%u:($0*0+%u) with labels center boxed notitle", seperator, i + 2, i + 2, i + 1); seperator = ",\\\n\t"; } fprintf(fp, "\n"); fprintf(fp, "pause -1\n"); fclose( fp ); fp = fopen( "link_trade_off_lookup.dat", "w" ); if( !fp ) return -1; for( i = 1; i < (1 << INSERTIONS_LOG2); i <<= 1 ) { unsigned int links; fprintf( fp, "%u", i ); for( links = 1; links <= MAX_LINKS; ++links ) { unsigned int j; skiplist_t *skiplist; struct timespec start, end; skiplist = skiplist_create( SKIPLIST_PROPERTY_NONE, links, int_compare, int_fprintf, NULL ); if( !skiplist ) return -1; for( j = 0; j < i; ++j ) if( skiplist_insert( skiplist, j ) ) return -1; time_stamp( &start ); for( j = 0; j < i; ++j ) if( !skiplist_contains( skiplist, j, NULL ) ) return -1; time_stamp( &end ); fprintf( fp, "\t%f", time_diff_ns( &start, &end ) / (double)i ); skiplist_destroy( skiplist ); } fprintf( fp, "\n" ); } fclose( fp ); #undef MAX_LINKS #undef INSERTIONS_LOG2 return 0; }
/* * this is pretty ugly, but it is used to find a free spot in the * tree for a new iommu allocation. We start from a given * hint and try to find an aligned range of a given size. * * Send the slot pointer, and we'll update it with the location * we found. * * This will return -EAGAIN if we found a good spot but someone * raced in and allocated it before we could. This gives the * caller the chance to update their hint. * * This will return -EEXIST if we couldn't find anything at all * * returns 0 if all went well, or some other negative error * if things went badly. */ int skiplist_insert_hole(struct sl_list *list, unsigned long hint, unsigned long limit, unsigned long size, unsigned long align, struct sl_slot *slot, gfp_t gfp_mask) { unsigned long last_end = 0; struct sl_node *p; struct sl_leaf *leaf; int i; int ret = -EEXIST; int preload_token; int pending_level; preload_token = skiplist_preload(list, gfp_mask); if (preload_token < 0) { return preload_token; } pending_level = pending_insert_level(preload_token); /* step one, lets find our hint */ rcu_read_lock(); again: last_end = max(last_end, hint); last_end = align_start(last_end, align); slot->key = align_start(hint, align); slot->size = size; leaf = __skiplist_lookup_leaf(list, &p, hint, 1); if (!p) p = list->head; if (leaf && !verify_key_in_leaf(leaf, hint, size)) { goto again; } again_lock: sl_lock_node(p); if (sl_node_dead(p)) { sl_unlock_node(p); goto again; } if (p != list->head) { leaf = sl_entry(p); /* * the leaf we found was past the hint, * go back one */ if (sl_max_key(leaf) > hint) { struct sl_node *locked = p; p = p->ptrs[0].prev; sl_unlock_node(locked); goto again_lock; } last_end = align_start(sl_max_key(sl_entry(p)), align); } /* * now walk at level 0 and find a hole. We could use lockless walks * if we wanted to bang more on the insertion code, but this * instead holds the lock on each node as we inspect it * * This is a little sloppy, insert will return -eexist if we get it * wrong. */ while(1) { leaf = sl_next_leaf(list, p, 0); if (!leaf) break; /* p and leaf are locked */ sl_lock_node(&leaf->node); if (last_end > sl_max_key(leaf)) goto next; for (i = 0; i < leaf->nr; i++) { if (last_end > leaf->keys[i]) continue; if (leaf->keys[i] - last_end >= size) { if (last_end + size > limit) { sl_unlock_node(&leaf->node); goto out_rcu; } sl_unlock_node(p); slot->key = last_end; slot->size = size; goto try_insert; } last_end = leaf->keys[i] + leaf->ptrs[i]->size; last_end = align_start(last_end, align); if (last_end + size > limit) { sl_unlock_node(&leaf->node); goto out_rcu; } } next: sl_unlock_node(p); p = &leaf->node; } if (last_end + size <= limit) { sl_unlock_node(p); slot->key = last_end; slot->size = size; goto try_insert; } out_rcu: /* we've failed */ sl_unlock_node(p); rcu_read_unlock(); preempt_enable(); return ret; try_insert: /* * if the pending_level is zero or there is room in the * leaf, we're ready to insert. This is true most of the * time, and we won't have to drop our lock and give others * the chance to race in and steal our spot. */ if (leaf && (pending_level == 0 || leaf->nr < SKIP_KEYS_PER_NODE) && !sl_node_dead(&leaf->node) && (slot->key >= sl_min_key(leaf) && slot->key + slot->size <= sl_max_key(leaf))) { ret = find_or_add_key(list, slot->key, size, leaf, slot, preload_token, NULL); rcu_read_unlock(); goto out; } /* * no such luck, drop our lock and try the insert the * old fashioned way */ if (leaf) sl_unlock_node(&leaf->node); rcu_read_unlock(); ret = skiplist_insert(list, slot, preload_token, NULL); out: /* * if we get an EEXIST here, it just means we lost the race. * return eagain to the caller so they can update the hint */ if (ret == -EEXIST) ret = -EAGAIN; preempt_enable(); return ret; }
/** * @brief TEST_CASE - Confirms incorrect inputs are handled gracefully for skiplist_insert. */ static int abuse_skiplist_insert( void ) { if( !skiplist_insert( NULL, 0 ) ) return -1; return 0; }
static int test_stable_sort() { #define RECORDS 32 int i; int result; int index1; int index2; int delete_count; int total_delete_count; Skiplist sl; SkiplistIterator iterator; Record records[RECORDS]; Record *record; Record target; void *value; instance_count = 0; result = skiplist_init_ex(&sl, 12, compare_record, free_test_func, 128, skiplist_type); if (result != 0) { return result; } for (i=0; i<RECORDS; i++) { records[i].line = i + 1; records[i].key = i + 1; } for (i=0; i<RECORDS/4; i++) { index1 = (RECORDS - 1) * (int64_t)rand() / (int64_t)RAND_MAX; index2 = RECORDS - 1 - index1; if (index1 != index2) { records[index1].key = records[index2].key; } } for (i=0; i<RECORDS; i++) { if ((result=skiplist_insert(&sl, records + i)) != 0) { return result; } instance_count++; } assert(instance_count == RECORDS); for (i=0; i<RECORDS; i++) { value = skiplist_find(&sl, records + i); assert(value != NULL && ((Record *)value)->key == records[i].key); } i = 0; skiplist_iterator(&sl, &iterator); while ((value=skiplist_next(&iterator)) != NULL) { i++; record = (Record *)value; printf("%d => #%d\n", record->key, record->line); } assert(i==RECORDS); target.key = 10; target.line = 0; if (skiplist_find_all(&sl, &target, &iterator) == 0) { printf("found key: %d\n", target.key); } i = 0; while ((value=skiplist_next(&iterator)) != NULL) { i++; record = (Record *)value; printf("%d => #%d\n", record->key, record->line); } printf("found record count: %d\n", i); total_delete_count = 0; for (i=0; i<RECORDS; i++) { if ((result=skiplist_delete_all(&sl, records + i, &delete_count)) == 0) { total_delete_count += delete_count; } assert((result == 0 && delete_count > 0) || (result != 0 && delete_count == 0)); } assert(total_delete_count == RECORDS); assert(instance_count == 0); i = 0; skiplist_iterator(&sl, &iterator); while ((value=skiplist_next(&iterator)) != NULL) { i++; } assert(i == 0); skiplist_destroy(&sl); assert(instance_count == 0); return 0; }
/** * @brief TEST_CASE - Sanity test of some key skiplist APIs using a pointer to data items. */ static int pointers( void ) { skiplist_t *skiplist; const coord_t coords[] = { /* Simple in order insertion. */ {5,5}, {7,5}, /* Duplicate x with increasing y. */ {5,6}, {5,8}, /* Duplicate x with decreasing y. */ {7,4}, {7,0}, /* Decreasing x. */ {4,5}, {3,5}, /* Increasing x. */ {9,0}, {10,0}, /* Duplicate values. */ {9,0}, {5,5}, /* Zero. */ {0,0}, /* Huge. */ {UINT_MAX,UINT_MAX} }; unsigned int i; skiplist_node_t *iter; coord_t tmp; skiplist = skiplist_create( SKIPLIST_PROPERTY_NONE, 8, coord_compare, coord_fprintf, NULL ); if( !skiplist ) return -1; for( i = 0; i < sizeof(coords) / sizeof(coords[0]); ++i ) if( skiplist_insert( skiplist, (uintptr_t) &coords[i] ) ) return -1; /* Output skiplist for debugging purposes. */ if( skiplist_fprintf_filename( "pointers.dot", skiplist ) ) return -1; /* Confirm skiplist is in the correct order. */ tmp.x = 0; tmp.y = 0; for( iter = skiplist_begin( skiplist ); iter != skiplist_end(); iter = skiplist_next( iter ) ) { coord_t *cur = (coord_t *)skiplist_node_value( iter, NULL ); if( cur->x < tmp.x ) return -1; if( cur->x == tmp.x && cur->y < tmp.y ) return -1; tmp = *cur; } /* Confirm the skiplist contains what we expect. */ for( i = 0; i < sizeof(coords) / sizeof(coords[0]); ++i ) if( !skiplist_contains( skiplist, (uintptr_t) &coords[i], NULL ) ) return -1; /* If we use a different pointer to point to the same values the skiplist should skill contain it. */ tmp = coords[0]; if( !skiplist_contains( skiplist, (uintptr_t) &tmp, NULL ) ) return -1; /* Free resources. */ skiplist_destroy( skiplist ); return 0; }
int main(void) { int i; struct timeval start, end; int *key = (int *)malloc(N * sizeof(int)); if (key == NULL) { exit(-1); } struct skiplist *list = skiplist_new(); if (list == NULL) { exit(-1); } printf("Test start!\n"); printf("Add %d nodes...\n", N); /* Insert test */ srandom(time(NULL)); gettimeofday(&start, NULL); for (i = 0; i < N; i++) { int value = key[i] = (int)random(); skiplist_insert(list, key[i], value); } gettimeofday(&end, NULL); printf("time span: %ldms\n", (end.tv_sec - start.tv_sec)*1000 + (end.tv_usec - start.tv_usec)/1000); #ifdef SKIPLIST_DEBUG skiplist_dump(list); #endif /* Search test 1 */ printf("Now search each node by key...\n"); gettimeofday(&start, NULL); for (i = 0; i < N; i++) { struct skipnode *node = skiplist_search_by_key(list, key[i]); if (node != NULL) { #ifdef SKIPLIST_DEBUG printf("key:0x%08x value:0x%08x\n", node->key, node->value); #endif } else { printf("Not found:0x%08x\n", key[i]); } #ifdef SKIPLIST_DEBUG printf("key rank:%d\n", skiplist_key_rank(list, key[i])); #else //skiplist_key_rank(list, key[i]); #endif } gettimeofday(&end, NULL); printf("time span: %ldms\n", (end.tv_sec - start.tv_sec)*1000 + (end.tv_usec - start.tv_usec)/1000); /* Search test 2 */ printf("Now search each node by rank...\n"); gettimeofday(&start, NULL); for (i = 0; i < N; i++) { struct skipnode *node = skiplist_search_by_rank(list, i + 1); if (node != NULL) { #ifdef SKIPLIST_DEBUG printf("rank:%d value:0x%08x\n", i + 1, node->value); #endif } else { printf("Not found:%d\n", i + 1); } } gettimeofday(&end, NULL); printf("time span: %ldms\n", (end.tv_sec - start.tv_sec)*1000 + (end.tv_usec - start.tv_usec)/1000); /* Delete test */ printf("Now remove all nodes...\n"); gettimeofday(&start, NULL); for (i = 0; i < N; i++) { skiplist_remove(list, key[i]); } gettimeofday(&end, NULL); printf("time span: %ldms\n", (end.tv_sec - start.tv_sec)*1000 + (end.tv_usec - start.tv_usec)/1000); #ifdef SKIPLIST_DEBUG skiplist_dump(list); #endif printf("End of Test.\n"); skiplist_delete(list); return 0; }
static int __init skiplist_init(void) { int i; struct timeval start, end; struct skiplist *list; struct skipnode *node; int res = 0; int *key = kmalloc(N * sizeof(int), GFP_KERNEL); if (key == NULL) { printk("-ENOMEM\n"); return -1; } printk("Starting initialization...\n"); list = skiplist_create(); if (list == NULL) { printk("-ENOMEM\n"); return -1; } printk("Started initialization...\n"); printk("Test start!\n"); /* Test 01 */ printk("Test 01: adding and search %d nodes testing!\n", N); printk("Add %d nodes...\n", N); do_gettimeofday(&start); for (i = 0; i < N; i++) { int value = key[i] = i; skiplist_insert(list, key[i], value); } do_gettimeofday(&end); printk("time span:% ldms\n", (end.tv_sec - start.tv_sec)*1000 + (end.tv_usec - start.tv_usec)/1000); #ifdef SKIPLIST_DEBUG skiplist_dump(list); #endif /* Search test */ printk("Now search %d node...\n", N); do_gettimeofday(&start); for (i = 0; i < N; i++) { struct skipnode *node = skiplist_search(list, key[i]); if (node != NULL) { #ifdef SKIPLIST_DEBUG printk("key:%d value:%d\n", node->key, node->value); #endif } else { printk("Not found:%d\n", key[i]); res = 1; break; } } do_gettimeofday(&end); printk("time span:% ldms\n", (end.tv_sec - start.tv_sec)*1000 + (end.tv_usec - start.tv_usec)/1000); if (res) { printk("Test 01: failed!\n"); goto out_clean; } else { printk("Test 01: success!\n"); } /* Test 02 */ printk("Test 02: search single node (%d/2) testing!\n", N); node = skiplist_search(list, N/2); if (node && node->value == N/2) { printk("Test 02: Success!\n"); } else { printk("Test 02: Failed!\n"); res = 1; goto out_clean; } /* Test 03 */ printk("Test 03: remove single node (%d/2) testing!\n", N); skiplist_remove(list, N/2); node = skiplist_search(list, N/2); if (!node) { printk("Test 03: Success!\n"); } else { printk("Test 03: Failed (key:%d)!\n", node->key); res = 1; goto out_clean; } /* Test 04 */ printk("Test 04: search single node equal or great than (%d/2) testing!\n", N); printk("Test 04: case 1: no equal node (%d/2) \n", N); node = skiplist_search_first_eq_big(list, N/2); if (!node || node->value != (N/2 + 1)) { printk("Test 04: Failed!\n"); res = 1; goto out_clean; } printk("Test 04: case 2: has equal node (%d/2 + 1) \n", N); node = skiplist_search_first_eq_big(list, N/2 + 1); if (node && node->value == (N/2 + 1)) { printk("Test 04: Success!\n"); } else { printk("Test 04: Failed!\n"); res = 1; goto out_clean; } /* Test 05 */ res = 0; printk("Test 05: remove all nodes\n"); for (i = 0; i < N; i++) { skiplist_remove(list, key[i]); } for (i = 0; i < N; i++) { node = skiplist_search(list, key[i]); if (node) { res = 1; break; } } if (res) printk("Test 05: Failed!\n"); else printk("Test 05: Success!\n"); #ifdef SKIPLIST_DEBUG skiplist_dump(list); #endif printk("End of Test.\n"); out_clean: skiplist_destroy(list); return 0; }