void hashtable_install( obj table, obj hash, obj key, obj value ) { obj vec, bucket; UINT_32 i; inserting_one( table ); vec = gvec_read( table, HASHTABLE_DIRECTORY ); for (bucket = read_dir(vec,hash); !EQ(bucket,FALSE_OBJ); bucket = gvec_read( bucket, BUCKET_OVERFLOW )) { for (i=SLOT(2); i<SLOT(2+BUCKET_CAPACITY); i+=sizeof(obj)) { if (EQ(read_bucket_hash(bucket,i),FALSE_OBJ)) { write_bucket_hash( bucket, i, hash ); write_bucket_key( bucket, i, key ); write_bucket_value( bucket, i, value ); return; } } } /* grow things... */ split_bucket( table, read_dir( vec, hash ), hash, key, value ); }
// insert 'key' into 'table', if it's not in there already // returns true if insertion succeeds, false if it was already in there bool xtndbln_hash_table_insert(XtndblNHashTable *table, int64 key) { assert(table); int start_time = clock(); // Start timing // Calculate table address int hash = h1(key); int address = rightmostnbits(table->depth, hash); int i, no_keys = table->buckets[address]->nkeys; // Check whether the key have been inserted or not if (table->buckets[address]->nkeys > 0) { table->stats.time += clock() - start_time; // Add time elapsed // Iterate through the keys in this bucket for (i = 0; i < no_keys; i++) { // The key have been inserted before if (table->buckets[address]->keys[i] == key) { // add time elapsed to total CPU time before returning table->stats.time += clock() - start_time; return false; } } } // If not, try to insert the key to the table while (table->buckets[address]->nkeys == table->bucketsize) { split_bucket(table, address); // and recalculate address because we might now need more bits address = rightmostnbits(table->depth, hash); } no_keys = table->buckets[address]->nkeys; // There is now space! Just insert the key table->buckets[address]->keys[no_keys] = key; table->buckets[address]->nkeys++; table->stats.nkeys++; // add time elapsed to total CPU time before returning table->stats.time += clock() - start_time; return true; }
// insert 'key' into 'table', if it's not in there already // returns true if insertion succeeds, false if it was already in there bool xuckoo_hash_table_insert(XuckooHashTable *table, int64 key) { assert(table); int start_time = clock(); int total_kicked_keys = 0, hash; int64 kick_key; InnerTable *innertable = table->table1; // Calculate table address int hash_1 = h1(key), hash_2 = h2(key); int address_1 = rightmostnbits(table->table1->depth, hash_1); int address_2 = rightmostnbits(table->table2->depth, hash_2); int address = address_1; // Check the key in table 1 if (table->table1->buckets[address_1]->full && table->table1->buckets[address_1]->key == key) { table->stats.time += clock() - start_time; // Add time elapsed return false; } // Check the key in table 2 if (table->table2->buckets[address_2]->full && table->table2->buckets[address_2]->key == key) { table->stats.time += clock() - start_time; // Add time elapsed return false; } // Check which table that has the least keys int insert_table = 1; if (table->table1->nkeys > table->table2->nkeys) { insert_table = 2; } // Try to find an empty slot and check if there is a cycle while (innertable->buckets[address]->full && total_kicked_keys != 2*(table->table1->size)) { kick_key = innertable->buckets[address]->key; total_kicked_keys++; innertable->buckets[address]->key = key; key = kick_key; // If it kicked the key from the Table 1, need to insert the kicked // key to the Table 2 if (insert_table == 1) { // Mark the next table that will be visited is table 2 insert_table = 2; // Update the temp_table and the hash innertable = table->table2; address = rightmostnbits(innertable->depth, h2(key)); } // If it kicked the key from the Table 2, need to insert the kicked // key to the Table 1 else if (insert_table == 2) { // Mark the next table that will be visited is table 2 insert_table = 1; // Update the temp_table and the hash innertable = table->table1; address = rightmostnbits(innertable->depth, h1(key)); } } // The number of kicked key is equal to the 2 times the table size, // indicates that there is a cycling, so we need to grow the table if (total_kicked_keys == 2*(table->table1->size)) { // Make space on the smallest size table to have space to // insert the key while (innertable->buckets[address]->full) { // If the first table has size smaller than or equal to the second // table's size, choose the first table if (table->table1->size <= table->table2->size) { innertable = table->table1; hash = h1(key); address = rightmostnbits(innertable->depth, hash); insert_table = 1; } // Otherwise, choose the second table else { innertable = table->table2; hash = h2(key); address = rightmostnbits(innertable->depth, hash); insert_table = 2; } split_bucket(innertable, address, insert_table); // and recalculate address because we might now need more bits address = rightmostnbits(innertable->depth, hash); } } // There is now space for the key, so we can just insert it innertable->buckets[address]->key = key; innertable->buckets[address]->full = true; innertable->nkeys++; table->stats.time += clock() - start_time; // Add time elapsed return true; }
int add(struct config *c, DB *dbp, ReadingSet *rs) { int cur_rec, ret; DBC *cursorp; struct rec_key cur_k; struct rec_val cur_v; DB_TXN *tid = NULL; unsigned char buf[POINT_OFF(MAXBUCKETRECS + NBUCKETSIZES)]; struct rec_val *v = (struct rec_val *)buf; struct point *rec_data = v->data; bool_t bucket_dirty = FALSE, bucket_valid = FALSE; unsigned long long dirty_start = ULLONG_MAX, dirty_end = 0; bzero(&cur_k, sizeof(cur_k)); bzero(&cur_v, sizeof(cur_v)); if ((ret = env->txn_begin(env, NULL, &tid, 0)) != 0) { error("txn_begin: %s\n", db_strerror(ret)); return -1; } if ((ret = dbp->cursor(dbp, tid, &cursorp, 0)) != 0) { error("db cursor: %s\n", db_strerror(ret)); goto abort; } if (cursorp == NULL) { dbp->err(dbp, ret, "cursor"); goto abort; } for (cur_rec = 0; cur_rec < rs->n_data; cur_rec++) { debug("Adding reading ts: 0x%x\n", rs->data[cur_rec]->timestamp); if (bucket_valid && v->n_valid > 0 && cur_k.stream_id == rs->streamid && cur_k.timestamp <= rs->data[cur_rec]->timestamp && cur_k.timestamp + v->period_length > rs->data[cur_rec]->timestamp) { /* we're already in the right bucket; don't need to do anything */ debug("In valid bucket. n_valid: %i\n", v->n_valid); } else { /* need to find the right bucket */ debug("Looking up bucket\n"); assert(!bucket_valid || POINT_OFF(v->n_valid) < sizeof(buf)); if (bucket_valid == TRUE && (ret = put(dbp, tid, &cur_k, v, POINT_OFF(v->n_valid))) < 0) { warn("error writing back data: %s\n", db_strerror(ret)); // we will loose data, aborto the transaction. goto abort; } bucket_valid = FALSE; cur_k.stream_id = rs->streamid; cur_k.timestamp = rs->data[cur_rec]->timestamp; if ((ret = get_bucket(cursorp, &cur_k, &cur_v)) <= 0) { /* create a new bucket */ /* the key has been updated by get_bucket */ v->n_valid = 0; v->period_length = bucket_sizes[-ret]; v->tail_timestamp = 0; debug("Created new bucket anchor: %i length: %i\n", cur_k.timestamp, v->period_length); } else { debug("Found existing bucket streamid: %i anchor: %i length: %i\n", cur_k.stream_id, cur_k.timestamp, v->period_length); if ((ret = get(cursorp, DB_SET | DB_RMW, &cur_k, v, sizeof(buf))) < 0) { warn("error reading bucket: %s\n", db_strerror(ret)); goto abort; } } bucket_valid = TRUE; } debug("v->: tail_timestamp: %i n_valid: %i\n", v->tail_timestamp, v->n_valid); /* start the insert -- we're in the current bucket */ if (v->tail_timestamp < rs->data[cur_rec]->timestamp || v->n_valid == 0) { /* if it's an append or a new bucket we can just write the values */ /* update the header block */ v->tail_timestamp = rs->data[cur_rec]->timestamp; v->n_valid++; /* and the data */ _rpc_copy_records(&v->data[v->n_valid-1], &rs->data[cur_rec], 1); debug("Append detected; inserting at offset: %i\n", POINT_OFF(v->n_valid-1)); } else { struct rec_val *v = (struct rec_val *)buf; struct point new_rec; int i; /* otherwise we have to insert it somewhere. we'll just read out all the data and do the insert stupidly. */ for (i = 0; i < v->n_valid; i++) { if (v->data[i].timestamp >= rs->data[cur_rec]->timestamp) break; } debug("Inserting within existing bucket index: %i (%i %i)\n", i, rs->data[cur_rec]->timestamp, v->tail_timestamp); /* appends should have been handled without reading back the whole record */ assert(i < v->n_valid); /* we have our insert position */ if (v->data[i].timestamp == rs->data[cur_rec]->timestamp) { /* replace a record */ debug("Replacing record with timestamp 0x%x\n", rs->data[cur_rec]->timestamp); _rpc_copy_records(&v->data[i], &rs->data[cur_rec], 1); } else { /* shift the existing records back */ debug("Inserting new record (moving %i recs)\n", v->n_valid - i); memmove(&v->data[i+1], &v->data[i], (v->n_valid - i) * sizeof(struct point)); _rpc_copy_records(&v->data[i], &rs->data[cur_rec], 1); v->n_valid++; /* and update the header */ } } bucket_dirty = TRUE; assert(v->n_valid < MAXBUCKETRECS + NBUCKETSIZES); if (v->n_valid > MAXBUCKETRECS) { debug("Splitting buckets since this one is full!\n"); /* start by writing the current bucket back */ assert(POINT_OFF(v->n_valid) < sizeof(buf)); if (bucket_valid == TRUE && (ret = put(dbp, tid, &cur_k, v, POINT_OFF(v->n_valid))) < 0) { bucket_valid = FALSE; warn("error writing back data: %s\n", db_strerror(ret)); goto abort; } if (split_bucket(dbp, cursorp, tid, &cur_k) < 0) goto abort; bzero(&cur_k, sizeof(cur_k)); bzero(&cur_v, sizeof(cur_v)); } /* find the time region this write dirties */ if (rs->data[cur_rec]->timestamp < dirty_start) { dirty_start = rs->data[cur_rec]->timestamp; } if (rs->data[cur_rec]->timestamp > dirty_end) { dirty_end = rs->data[cur_rec]->timestamp; } } if (bucket_valid && bucket_dirty) { debug("flushing bucket back to db\n"); assert(POINT_OFF(v->n_valid) < sizeof(buf)); if ((ret = put(dbp, tid, &cur_k, v, POINT_OFF(v->n_valid))) < 0) { warn("error writing back data: %s\n", db_strerror(ret)); goto abort; } } cursorp->close(cursorp); if ((ret = tid->commit(tid, 0)) != 0) { fatal("transaction commit failed: %s\n", db_strerror(ret)); // SDH : "If DB_TXN->commit() encounters an error, the transaction // and all child transactions of the transaction are aborted." // // So, we can just die here. // do_shutdown = 1; return -1; } // if (dirty_start != ULLONG_MAX && dirty_end != 0) { mark_sketch_dirty(c, rs->streamid, dirty_start, dirty_end); } return 0; abort: cursorp->close(cursorp); warn("Aborting transaction\n"); if ((ret = tid->abort(tid)) != 0) { fatal("Could not abort transaction: %s\n", db_strerror(ret)); // do_shutdown = 1; // SDH : there are no documented error codes for DB_TXN->abort(). assert(0); } return -1; }