int test_add_to_same_key(){ hashtable_t *ht = ht_create( 65536, free_jambo ); ht_set( ht, "key1", "blorg" ); ht_set( ht, "key1", "bloff" ); ht_set( ht, "key1", "gladd" ); ht_set( ht, "key1", "grutt" ); ht_set( ht, "key1", "twerky" ); ht_set( ht, "key1", "lennart" ); ht_set( ht, "key2", "sverker" ); ht_set( ht, "key3", "Rogge" ); ht_set( ht, "key4", "Swutt" ); check( strcmp( ht_get(ht, "key1"), "lennart") == 0 ); check( strcmp( ht_get(ht, "key2"), "sverker") == 0); check( strcmp( ht_get(ht, "key3"), "Rogge") == 0); check( strcmp( ht_get(ht, "key4"), "Swutt") == 0); check(ht_size(ht) == 4); ht_destroy(ht); return 0; }
void sm_resend(sm_t self, int fd) { sm_private_t my = self->private_state; sm_sendq_t sendq = ht_get_value(my->fd_to_sendq, HT_KEY(fd)); while (sendq) { char *head = sendq->head; char *tail = sendq->tail; // send as much as we can without blocking sm_on_debug(self, "ss.sendq<%p> resume send to fd=%d len=%zd", sendq, fd, (tail - head)); while (head < tail) { ssize_t sent_bytes = send(fd, (void*)head, (tail - head), 0); if (sent_bytes <= 0) { if (sent_bytes && errno != EWOULDBLOCK) { perror("sendq retry failed"); self->remove_fd(self, fd); return; } break; } head += sent_bytes; } sendq->head = head; if (head < tail) { // still have stuff to send sm_on_debug(self, "ss.sendq<%p> defer len=%zd", sendq, (tail - head)); break; } self->on_sent(self, fd, sendq->value, sendq->begin, tail - sendq->begin); sm_sendq_t nextq = sendq->next; ht_put(my->fd_to_sendq, HT_KEY(fd), nextq); if (!nextq) { FD_CLR(fd, my->send_fds); } int recv_fd = sendq->recv_fd; if (recv_fd && FD_ISSET(recv_fd, my->all_fds)) { // if no other sendq's match this blocked recv_fd, re-enable it bool found = false; if (ht_size(my->fd_to_sendq)) { sm_sendq_t *qs = (sm_sendq_t *)ht_values(my->fd_to_sendq); sm_sendq_t *q; for (q = qs; *q && !found; q++) { sm_sendq_t sq; for (sq = *q; sq && !found; sq = sq->next) { found |= (sq->recv_fd == recv_fd); } } free(qs); } if (!found) { sm_on_debug(self, "ss.sendq<%p> re-enable recv_fd=%d", sendq, recv_fd); FD_SET(recv_fd, my->recv_fds); // don't FD_SET(tmp_recv_fds), since maybe there was no input // instead, let the next select loop pick it up } } sm_on_debug(self, "ss.sendq<%p> free, next=<%p>", sendq, nextq); sm_sendq_free(sendq); sendq = nextq; } }
sm_status sm_remove_fd(sm_t self, int fd) { sm_private_t my = self->private_state; if (!FD_ISSET(fd, my->all_fds)) { return SM_ERROR; } void *value = ht_put(my->fd_to_value, HT_KEY(fd), NULL); bool is_server = FD_ISSET(fd, my->server_fds); sm_on_debug(self, "ss.remove%s_fd(%d)", (is_server ? "_server" : ""), fd); sm_status ret = self->on_close(self, fd, value, is_server); close(fd); FD_CLR(fd, my->all_fds); if (is_server) { FD_CLR(fd, my->server_fds); } FD_CLR(fd, my->send_fds); FD_CLR(fd, my->recv_fds); FD_CLR(fd, my->tmp_send_fds); FD_CLR(fd, my->tmp_recv_fds); FD_CLR(fd, my->tmp_fail_fds); if (fd == my->max_fd) { while (my->max_fd >= 0 && !FD_ISSET(my->max_fd, my->all_fds)) { my->max_fd--; } } if (ht_size(my->fd_to_sendq)) { sm_sendq_t *qs = (sm_sendq_t *)ht_values(my->fd_to_sendq); sm_sendq_t *q; for (q = qs; *q; q++) { sm_sendq_t sendq = *q; while (sendq) { if (sendq->recv_fd == fd) { sendq->recv_fd = 0; // don't abort this blocked send, even though the "cause" has ended } sendq = sendq->next; } } free(qs); } return ret; }
int main(int argc, char **argv) { struct option long_options[] = { // These options don't set a flag {"help", no_argument, NULL, 'h'}, {"alternate", no_argument, NULL, 'A'}, {"effective", required_argument, NULL, 'f'}, {"duration", required_argument, NULL, 'd'}, {"initial-size", required_argument, NULL, 'i'}, {"num-threads", required_argument, NULL, 'n'}, {"range", required_argument, NULL, 'r'}, {"seed", required_argument, NULL, 's'}, {"update-rate", required_argument, NULL, 'u'}, {"move-rate", required_argument, NULL, 'm'}, {"snapshot-rate", required_argument, NULL, 'a'}, {"elasticity", required_argument, NULL, 'x'}, {NULL, 0, NULL, 0} }; ht_intset_t *set; int i, c, size; val_t last = 0; val_t val = 0; unsigned long reads, effreads, updates, effupds, moves, snapshots, aborts, aborts_locked_read, aborts_locked_write, aborts_validate_read, aborts_validate_write, aborts_validate_commit, aborts_invalid_memory, aborts_double_write, max_retries, failures_because_contention; thread_data_t *data; pthread_t *threads; pthread_attr_t attr; barrier_t barrier; struct timeval start, end; struct timespec timeout; int duration = DEFAULT_DURATION; int initial = DEFAULT_INITIAL; int nb_threads = DEFAULT_NB_THREADS; long range = DEFAULT_RANGE; int seed = DEFAULT_SEED; int update = DEFAULT_UPDATE; int load_factor = DEFAULT_LOAD; int move = DEFAULT_MOVE; int snapshot = DEFAULT_SNAPSHOT; int unit_tx = DEFAULT_ELASTICITY; int alternate = DEFAULT_ALTERNATE; int effective = DEFAULT_EFFECTIVE; sigset_t block_set; while(1) { i = 0; c = getopt_long(argc, argv, "hAf:d:i:n:r:s:u:m:a:l:x:", long_options, &i); if(c == -1) break; if(c == 0 && long_options[i].flag == 0) c = long_options[i].val; switch(c) { case 0: // Flag is automatically set break; case 'h': printf("intset -- STM stress test " "(hash table)\n" "\n" "Usage:\n" " intset [options...]\n" "\n" "Options:\n" " -h, --help\n" " Print this message\n" " -A, --Alternate\n" " Consecutive insert/remove target the same value\n" " -f, --effective <int>\n" " update txs must effectively write (0=trial, 1=effective, default=" XSTR(DEFAULT_EFFECTIVE) ")\n" " -d, --duration <int>\n" " Test duration in milliseconds (0=infinite, default=" XSTR(DEFAULT_DURATION) ")\n" " -i, --initial-size <int>\n" " Number of elements to insert before test (default=" XSTR(DEFAULT_INITIAL) ")\n" " -n, --num-threads <int>\n" " Number of threads (default=" XSTR(DEFAULT_NB_THREADS) ")\n" " -r, --range <int>\n" " Range of integer values inserted in set (default=" XSTR(DEFAULT_RANGE) ")\n" " -s, --seed <int>\n" " RNG seed (0=time-based, default=" XSTR(DEFAULT_SEED) ")\n" " -u, --update-rate <int>\n" " Percentage of update transactions (default=" XSTR(DEFAULT_UPDATE) ")\n" " -m , --move-rate <int>\n" " Percentage of move transactions (default=" XSTR(DEFAULT_MOVE) ")\n" " -a , --snapshot-rate <int>\n" " Percentage of snapshot transactions (default=" XSTR(DEFAULT_SNAPSHOT) ")\n" " -l , --load-factor <int>\n" " Ratio of keys over buckets (default=" XSTR(DEFAULT_LOAD) ")\n" " -x, --elasticity (default=4)\n" " Use elastic transactions\n" " 0 = non-protected,\n" " 1 = normal transaction,\n" " 2 = read elastic-tx,\n" " 3 = read/add elastic-tx,\n" " 4 = read/add/rem elastic-tx,\n" " 5 = elastic-tx w/ optimized move.\n" ); exit(0); case 'A': alternate = 1; break; case 'f': effective = atoi(optarg); break; case 'd': duration = atoi(optarg); break; case 'i': initial = atoi(optarg); break; case 'n': nb_threads = atoi(optarg); break; case 'r': range = atol(optarg); break; case 's': seed = atoi(optarg); break; case 'u': update = atoi(optarg); break; case 'm': move = atoi(optarg); break; case 'a': snapshot = atoi(optarg); break; case 'l': load_factor = atoi(optarg); break; case 'x': unit_tx = atoi(optarg); break; case '?': printf("Use -h or --help for help\n"); exit(0); default: exit(1); } } assert(duration >= 0); assert(initial >= 0); assert(nb_threads > 0); assert(range > 0 && range >= initial); assert(update >= 0 && update <= 100); assert(move >= 0 && move <= update); assert(snapshot >= 0 && snapshot <= (100-update)); assert(initial < MAXHTLENGTH); assert(initial >= load_factor); printf("Set type : hash table\n"); printf("Duration : %d\n", duration); printf("Initial size : %d\n", initial); printf("Nb threads : %d\n", nb_threads); printf("Value range : %ld\n", range); printf("Seed : %d\n", seed); printf("Update rate : %d\n", update); printf("Load factor : %d\n", load_factor); printf("Move rate : %d\n", move); printf("Snapshot rate: %d\n", snapshot); printf("Elasticity : %d\n", unit_tx); printf("Alternate : %d\n", alternate); printf("Effective : %d\n", effective); printf("Type sizes : int=%d/long=%d/ptr=%d/word=%d\n", (int)sizeof(int), (int)sizeof(long), (int)sizeof(void *), (int)sizeof(uintptr_t)); timeout.tv_sec = duration / 1000; timeout.tv_nsec = (duration % 1000) * 1000000; if ((data = (thread_data_t *)malloc(nb_threads * sizeof(thread_data_t))) == NULL) { perror("malloc"); exit(1); } if ((threads = (pthread_t *)malloc(nb_threads * sizeof(pthread_t))) == NULL) { perror("malloc"); exit(1); } if (seed == 0) srand((int)time(0)); else srand(seed); //maxhtlength = (unsigned int) initial / load_factor; set = ht_new(); stop = 0; // Init STM printf("Initializing STM\n"); TM_STARTUP(); // Populate set printf("Adding %d entries to set\n", initial); i = 0; maxhtlength = (int) (initial / load_factor); while (i < initial) { val = rand_range(range); if (ht_add(set, val, 0)) { last = val; i++; } } size = ht_size(set); printf("Set size : %d\n", size); printf("Bucket amount: %d\n", maxhtlength); printf("Load : %d\n", load_factor); // Access set from all threads barrier_init(&barrier, nb_threads + 1); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for (i = 0; i < nb_threads; i++) { printf("Creating thread %d\n", i); data[i].first = last; data[i].range = range; data[i].update = update; data[i].load_factor = load_factor; data[i].move = move; data[i].snapshot = snapshot; data[i].unit_tx = unit_tx; data[i].alternate = alternate; data[i].effective = effective; data[i].nb_add = 0; data[i].nb_added = 0; data[i].nb_remove = 0; data[i].nb_removed = 0; data[i].nb_move = 0; data[i].nb_moved = 0; data[i].nb_snapshot = 0; data[i].nb_snapshoted = 0; data[i].nb_contains = 0; data[i].nb_found = 0; data[i].nb_aborts = 0; data[i].nb_aborts_locked_read = 0; data[i].nb_aborts_locked_write = 0; data[i].nb_aborts_validate_read = 0; data[i].nb_aborts_validate_write = 0; data[i].nb_aborts_validate_commit = 0; data[i].nb_aborts_invalid_memory = 0; data[i].nb_aborts_double_write = 0; data[i].max_retries = 0; data[i].seed = rand(); data[i].set = set; data[i].barrier = &barrier; data[i].failures_because_contention = 0; if (pthread_create(&threads[i], &attr, test, (void *)(&data[i])) != 0) { fprintf(stderr, "Error creating thread\n"); exit(1); } } pthread_attr_destroy(&attr); // Start threads barrier_cross(&barrier); printf("STARTING...\n"); gettimeofday(&start, NULL); if (duration > 0) { nanosleep(&timeout, NULL); } else { sigemptyset(&block_set); sigsuspend(&block_set); } AO_store_full(&stop, 1); gettimeofday(&end, NULL); printf("STOPPING...\n"); // Wait for thread completion for (i = 0; i < nb_threads; i++) { if (pthread_join(threads[i], NULL) != 0) { fprintf(stderr, "Error waiting for thread completion\n"); exit(1); } } duration = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000); aborts = 0; aborts_locked_read = 0; aborts_locked_write = 0; aborts_validate_read = 0; aborts_validate_write = 0; aborts_validate_commit = 0; aborts_invalid_memory = 0; aborts_double_write = 0; failures_because_contention = 0; reads = 0; effreads = 0; updates = 0; effupds = 0; moves = 0; snapshots = 0; max_retries = 0; for (i = 0; i < nb_threads; i++) { printf("Thread %d\n", i); printf(" #add : %lu\n", data[i].nb_add); printf(" #added : %lu\n", data[i].nb_added); printf(" #remove : %lu\n", data[i].nb_remove); printf(" #removed : %lu\n", data[i].nb_removed); printf(" #contains : %lu\n", data[i].nb_contains); printf(" #found : %lu\n", data[i].nb_found); printf(" #move : %lu\n", data[i].nb_move); printf(" #moved : %lu\n", data[i].nb_moved); printf(" #snapshot : %lu\n", data[i].nb_snapshot); printf(" #snapshoted : %lu\n", data[i].nb_snapshoted); printf(" #aborts : %lu\n", data[i].nb_aborts); printf(" #lock-r : %lu\n", data[i].nb_aborts_locked_read); printf(" #lock-w : %lu\n", data[i].nb_aborts_locked_write); printf(" #val-r : %lu\n", data[i].nb_aborts_validate_read); printf(" #val-w : %lu\n", data[i].nb_aborts_validate_write); printf(" #val-c : %lu\n", data[i].nb_aborts_validate_commit); printf(" #inv-mem : %lu\n", data[i].nb_aborts_invalid_memory); printf(" #dup-w : %lu\n", data[i].nb_aborts_double_write); printf(" #failures : %lu\n", data[i].failures_because_contention); printf(" Max retries : %lu\n", data[i].max_retries); aborts += data[i].nb_aborts; aborts_locked_read += data[i].nb_aborts_locked_read; aborts_locked_write += data[i].nb_aborts_locked_write; aborts_validate_read += data[i].nb_aborts_validate_read; aborts_validate_write += data[i].nb_aborts_validate_write; aborts_validate_commit += data[i].nb_aborts_validate_commit; aborts_invalid_memory += data[i].nb_aborts_invalid_memory; aborts_double_write += data[i].nb_aborts_double_write; failures_because_contention += data[i].failures_because_contention; reads += (data[i].nb_contains + data[i].nb_snapshot); effreads += data[i].nb_contains + (data[i].nb_add - data[i].nb_added) + (data[i].nb_remove - data[i].nb_removed) + (data[i].nb_move - data[i].nb_moved) + data[i].nb_snapshoted; updates += (data[i].nb_add + data[i].nb_remove + data[i].nb_move); effupds += data[i].nb_removed + data[i].nb_added + data[i].nb_moved; moves += data[i].nb_move; snapshots += data[i].nb_snapshot; size += data[i].nb_added - data[i].nb_removed; if (max_retries < data[i].max_retries) max_retries = data[i].max_retries; } printf("Set size : %d (expected: %d)\n", ht_size(set), size); printf("Duration : %d (ms)\n", duration); printf("#txs : %lu (%f / s)\n", reads + updates + moves + snapshots, (reads + updates + moves + snapshots) * 1000.0 / duration); printf("#read txs : "); if (effective) { printf("%lu (%f / s)\n", effreads, effreads * 1000.0 / duration); printf(" #cont/snpsht: %lu (%f / s)\n", reads, reads * 1000.0 / duration); } else printf("%lu (%f / s)\n", reads, reads * 1000.0 / duration); printf("#eff. upd rate: %f \n", 100.0 * effupds / (effupds + effreads)); printf("#update txs : "); if (effective) { printf("%lu (%f / s)\n", effupds, effupds * 1000.0 / duration); printf(" #upd trials : %lu (%f / s)\n", updates, updates * 1000.0 / duration); } else printf("%lu (%f / s)\n", updates, updates * 1000.0 / duration); printf("#move txs : %lu (%f / s)\n", moves, moves * 1000.0 / duration); printf("#snapshot txs : %lu (%f / s)\n", snapshots, snapshots * 1000.0 / duration); printf("#aborts : %lu (%f / s)\n", aborts, aborts * 1000.0 / duration); printf(" #lock-r : %lu (%f / s)\n", aborts_locked_read, aborts_locked_read * 1000.0 / duration); printf(" #lock-w : %lu (%f / s)\n", aborts_locked_write, aborts_locked_write * 1000.0 / duration); printf(" #val-r : %lu (%f / s)\n", aborts_validate_read, aborts_validate_read * 1000.0 / duration); printf(" #val-w : %lu (%f / s)\n", aborts_validate_write, aborts_validate_write * 1000.0 / duration); printf(" #val-c : %lu (%f / s)\n", aborts_validate_commit, aborts_validate_commit * 1000.0 / duration); printf(" #inv-mem : %lu (%f / s)\n", aborts_invalid_memory, aborts_invalid_memory * 1000.0 / duration); printf(" #dup-w : %lu (%f / s)\n", aborts_double_write, aborts_double_write * 1000.0 / duration); printf(" #failures : %lu\n", failures_because_contention); printf("Max retries : %lu\n", max_retries); // Delete set ht_delete(set); // Cleanup STM TM_SHUTDOWN(); free(threads); free(data); return 0; }
/** * Run after gunderscript_build_file() to export the compiled bytecode to an * external bytecode file. * instance: a Gunderscript object. * fileName: The name of the file to export to. Caution: file will be overwritten * returns: true upon success, or false if file cannot be opened, * no code has been built, or there was an error building code. */ GSAPI bool gunderscript_export_bytecode(Gunderscript * instance, char * fileName) { FILE * outFile = fopen(fileName, "w"); GSByteCodeHeader header; HTIter functionHTIter; char * byteCodeBuffer; /* check if file open fails */ if(outFile == NULL) { instance->err = GUNDERSCRIPTERR_BAD_FILE_OPEN_WRITE; return false; } /* create header */ strcpy(header.header, GS_BYTECODE_HEADER); strcpy(header.buildDate, __DATE__); header.byteCodeLen = vm_bytecode_size(instance->vm); header.numFunctions = ht_size(vm_functions(instance->vm)); /* write header to file, return false on failure */ if(fwrite(&header, sizeof(GSByteCodeHeader), 1, outFile) != 1) { instance->err = GUNDERSCRIPTERR_BAD_FILE_WRITE; return false; } ht_iter_get(vm_functions(instance->vm), &functionHTIter); /* write exported functions to file */ while(ht_iter_has_next(&functionHTIter)) { DSValue value; char functionName[GS_MAX_FUNCTION_NAME_LEN]; size_t functionNameLen; char outLen; /* get the next item from hashtable */ ht_iter_next(&functionHTIter, functionName, GS_MAX_FUNCTION_NAME_LEN, &value, &functionNameLen, false); /* check if the function should be exported. if so, write it to the file */ if(((VMFunc*)value.pointerVal)->exported) { /* TODO: create an error handler case for this */ assert(functionNameLen < GS_MAX_FUNCTION_NAME_LEN); /* write it's name and length to the file */ outLen = functionNameLen; if(fwrite(&outLen, sizeof(char), 1, outFile) != 1 || fwrite(functionName, sizeof(char), functionNameLen, outFile) != functionNameLen) { instance->err = GUNDERSCRIPTERR_BAD_FILE_WRITE; return false; } /* write its CompilerFunc to the file (stores function call information) */ if(fwrite(value.pointerVal, sizeof(VMFunc), 1, outFile) != 1) { instance->err = GUNDERSCRIPTERR_BAD_FILE_WRITE; return false; } } } byteCodeBuffer = vm_bytecode(instance->vm); if(byteCodeBuffer == NULL) { instance->err = GUNDERSCRIPTERR_NO_SUCCESSFUL_BUILD; return false; } /* write bytecode */ if(fwrite(byteCodeBuffer, vm_bytecode_size(instance->vm), 1, outFile) != 1) { instance->err = GUNDERSCRIPTERR_BAD_FILE_WRITE; return false; } fclose(outFile); return true; }
/** * A parse_function_definitions() subparser that parses the arguments from a function * definition. The function then stores the arguments in the current symbol * table and returns the number of arguments that were provided. * c: an instance of Compiler. * l: an instance of lexer. * returns: if the current token is a LEXERTYPE_KEYVAR, the parser starts to * parse function arguments. If not, it returns -1 and sets c->err to * COMPILERERR_SUCCESS. If an error occurred, or there is a mistake in the * script, the function returns -1, but c->err is set to a relevant error code. */ static int parse_arguments(Compiler * c, Lexer * l) { assert(c != NULL); assert(l != NULL); /* while the next token is not an end parenthesis and tokens remain, parse * the tokens and store each KEYVAR type token in the symbol table as a * function argument */ HT * symTbl = symtblstk_peek(c, 0); LexerType type; size_t len; bool prevExisted; char * token = lexer_next(l, &type, &len); DSValue value; int numArgs = 0; while(true) { /* check current token is an argument, if not throw a fit */ if(token == NULL || type != LEXERTYPE_KEYVAR) { /* if there was a closed parenth, there are no args, otherwise, give err */ if(tokens_equal(LANG_CPARENTH, LANG_CPARENTH_LEN, token, len)) { return 0; } else { c->err = COMPILERERR_EXPECTED_VARNAME; return -1; } } /* store variable along with index at which its data will be stored in the * frame stack in the virtual machine */ value.intVal = ht_size(symTbl); if(!ht_put_raw_key(symTbl, token, len, &value, NULL, &prevExisted)) { c->err = COMPILERERR_ALLOC_FAILED; return -1; } numArgs++; /* check for duplicate var names */ if(prevExisted) { c->err = COMPILERERR_PREV_DEFINED_VAR; return -1; } /* get next token and check for anything but a comma arg delimiter */ token = lexer_next(l, &type, &len); if(!tokens_equal(token, len, LANG_ARGDELIM, LANG_ARGDELIM_LEN)) { /* check for end of the args, or invalid token */ if(tokens_equal(token, len, LANG_CPARENTH, LANG_CPARENTH_LEN)) { /* end of args */ return numArgs; } else { c->err = COMPILERERR_UNEXPECTED_TOKEN; return -1; } } else { /* skip over the comma / arg delimiter */ token = lexer_next(l, &type, &len); } } return -1; }
int main(int argc, char *argv[]) { (void) argc; (void) argv; hash_table ht; ht_init(&ht, HT_KEY_CONST | HT_VALUE_CONST, 0.05); char *s1 = (char*)"teststring 1"; char *s2 = (char*)"teststring 2"; char *s3 = (char*)"teststring 3"; ht_insert(&ht, s1, strlen(s1)+1, s2, strlen(s2)+1); int contains = ht_contains(&ht, s1, strlen(s1)+1); test(contains, "Checking for key \"%s\"", s1); size_t value_size; char *got = ht_get(&ht, s1, strlen(s1)+1, &value_size); fprintf(stderr, "Value size: %zu\n", value_size); fprintf(stderr, "Got: {\"%s\": -----\"%s\"}\n", s1, got); test(value_size == strlen(s2)+1, "Value size was %zu (desired %lu)", value_size, strlen(s2)+1); fprintf(stderr, "Replacing {\"%s\": \"%s\"} with {\"%s\": \"%s\"}\n", s1, s2, s1, s3); ht_insert(&ht, s1, strlen(s1)+1, s3, strlen(s3)+1); unsigned int num_keys; void **keys; keys = ht_keys(&ht, &num_keys); test(num_keys == 1, "HashTable has %d keys", num_keys); test(keys != NULL, "Keys is not null"); if(keys) free(keys); got = ht_get(&ht, s1, strlen(s1)+1, &value_size); fprintf(stderr, "Value size: %zu\n", value_size); fprintf(stderr, "Got: {\"%s\": \"%s\"}\n", s1, got); test(value_size == strlen(s3)+1, "Value size was %zu (desired %lu)", value_size, strlen(s3)+1); fprintf(stderr, "Removing entry with key \"%s\"\n", s1); ht_remove(&ht, s1, strlen(s1)+1); contains = ht_contains(&ht, s1, strlen(s1)+1); test(!contains, "Checking for removal of key \"%s\"", s1); keys = ht_keys(&ht, &num_keys); test(num_keys == 0, "HashTable has %d keys", num_keys); if(keys) free(keys); fprintf(stderr, "Stress test"); int key_count = 1000000; int i; int *many_keys = malloc(key_count * sizeof(*many_keys)); int *many_values = malloc(key_count * sizeof(*many_values)); srand(time(NULL)); for(i = 0; i < key_count; i++) { many_keys[i] = i; many_values[i] = rand(); } struct timespec t1; struct timespec t2; t1 = snap_time(); for(i = 0; i < key_count; i++) { ht_insert(&ht, &(many_keys[i]), sizeof(many_keys[i]), &(many_values[i]), sizeof(many_values[i])); } t2 = snap_time(); fprintf(stderr, "Inserting %d keys took %.2f seconds\n", key_count, get_elapsed(t1, t2)); fprintf(stderr, "Checking inserted keys\n"); int ok_flag = 1; for(i = 0; i < key_count; i++) { if(ht_contains(&ht, &(many_keys[i]), sizeof(many_keys[i]))) { size_t value_size; int value; value = *(int*)ht_get(&ht, &(many_keys[i]), sizeof(many_keys[i]), &value_size); if(value != many_values[i]) { fprintf(stderr, "Key value mismatch. Got {%d: %d} expected: {%d: %d}\n", many_keys[i], value, many_keys[i], many_values[i]); ok_flag = 0; break; } } else { fprintf(stderr, "Missing key-value pair {%d: %d}\n", many_keys[i], many_values[i]); ok_flag = 0; break; } } test(ok_flag == 1, "Result was %d", ok_flag); ht_clear(&ht); ht_resize(&ht, 4194304); t1 = snap_time(); for(i = 0; i < key_count; i++) { ht_insert(&ht, &(many_keys[i]), sizeof(many_keys[i]), &(many_values[i]), sizeof(many_values[i])); } t2 = snap_time(); fprintf(stderr, "Inserting %d keys (on preallocated table) took %.2f seconds\n", key_count, get_elapsed(t1, t2)); for(i = 0; i < key_count; i++) { ht_remove(&ht, &(many_keys[i]), sizeof(many_keys[i])); } test(ht_size(&ht) == 0, "%d keys remaining", ht_size(&ht)); ht_destroy(&ht); free(many_keys); free(many_values); return report_results(); }