void HashMap<Key, T, Hasher, EqualKey, Alloc>:: refillBucket(){ NodePointer pprev = static_cast<NodePointer>(&bucket_start_node_); NodePointer pcurr = pprev->next_; if (pcurr != nullptr) { size_type curr_index = bucketIndex(pcurr->hash_); bucket_list_[curr_index] = pcurr; //size_type prev_index = curr_index; for (pprev = pcurr, pcurr = pcurr->next_; pcurr != nullptr; pcurr = pprev->next_) { curr_index = bucketIndex(pcurr->hash_); //if (curr_index == prev_index) { // pprev = pcurr; // continue; //} if (bucket_list_[curr_index] == nullptr) { bucket_list_[curr_index] = pprev; pprev = pcurr; //prev_index = curr_index; } else{ NodePointer pp = pcurr; NodePointer np = pcurr->next_; Key curkey = pcurr->value_.first; Key nkey = np->value_.first; for(; np != nullptr && key_equal()(curkey, nkey); np = np->next_,nkey = np->value_.first,pp = pp->next_) ; pprev->next_ = np; pp->next_ = bucket_list_[curr_index]->next_; bucket_list_[curr_index]->next_ = pcurr; } } } }
iterator HashMap<Key, T, Hasher, EqualKey, Alloc>:: addNode(const ValueType& v){ NodePointer np = new Node(); np->hash_ = hash_function()(v.first); np->value_ = v; if (size_ + 1 > max_load_) { rehash(size_+1); } size_type index = bucketIndex(np->hash_, bucket_count_); if (bucket_list_[index] == nullptr) { NodePointer pStart = static_cast<NodePointer>(&bucket_start_node_); np->next_ = pStart->next_; pStart->next_ = np; bucket_list_[index] = pStart; if (np->next_ != nullptr) { bucket_list_[bucketIndex(np->next_->hash_, bucket_count_)] = np->next_; } } else{ np->next_ = bucket_list_[index]->next_; bucket_list_[index]->next_ = np; } ++size_; return iterator(np); }
// Timer IDs are implemented using a free-list; // there's a vector initialized with: // X[i] = i + 1 // and nextFreeTimerId starts with 1. // // Allocating a timer ID involves taking the ID from // X[nextFreeTimerId] // updating nextFreeTimerId to this value and returning the old value // // When the timer ID is allocated, its cell in the vector is unused (it's a // free list). As an added protection, we use the cell to store an invalid // (negative) value that we can later check for integrity. // // (continues below). int QAbstractEventDispatcherPrivate::allocateTimerId() { int timerId, newTimerId; int at, *b; do { timerId = nextFreeTimerId; //.loadAcquire(); // ### FIXME Proper memory ordering semantics // which bucket are we looking in? int which = timerId & TimerIdMask; int bucket = bucketOffset(which); at = bucketIndex(bucket, which); b = timerIds[bucket]; if (!b) { // allocate a new bucket b = allocateBucket(bucket); if (!timerIds[bucket].testAndSetRelease(0, b)) { // another thread won the race to allocate the bucket delete [] b; b = timerIds[bucket]; } } newTimerId = prepareNewValueWithSerialNumber(timerId, b[at]); } while (!nextFreeTimerId.testAndSetRelaxed(timerId, newTimerId)); b[at] = -timerId; return timerId; }
int QAbstractEventDispatcherPrivate::allocateTimerId() { int timerId, newTimerId; do { timerId = nextFreeTimerId; // which bucket are we looking in? int which = timerId & 0x00ffffff; int bucket = bucketOffset(which); int at = bucketIndex(bucket, which); int *b = timerIds[bucket]; if (!b) { // allocate a new bucket b = allocateBucket(bucket); if (!timerIds[bucket].testAndSetRelease(0, b)) { // another thread won the race to allocate the bucket delete [] b; b = timerIds[bucket]; } } newTimerId = b[at]; } while (!nextFreeTimerId.testAndSetRelaxed(timerId, newTimerId)); return timerId; }
NodePointer HashMap<Key, T, Hasher, EqualKey, Alloc>:: findNode(const Key& k) const{ if (bucket_count_ == 0) { return nullptr; } std::size_t h = hash_function()(k); size_type curr_index = bucketIndex(h, bucket_count_); NodePointer np = bucket_list_[curr_index]; while(np != nullptr) { np = np->next_; if (bucketIndex(np->hash_, bucket_count_) == curr_index && key_equal()(np->value_.first, k) ) { break; } } return np; }
int Hash_insert(Hash *self, const void *key, const void *value) { size_t i = bucketIndex(self, key); if(!self->buckets[i]){ self->buckets[i] = newNode(self, key, value); return self->buckets[i]? 0:1; } return bucketInsert(self, i, key, value); }
size_type HashMap<Key, T, Hasher, EqualKey, Alloc>:: bucket_size(size_type n) const{ NodePointer np = bucket_list_[n]; size_type rsize = 0; while(np != nullptr){ np = np->next_; if (bucketIndex(np->hash_, bucket_count_) ) ++rsize; } return rsize; }
void HashMap<Key, T, Hasher, EqualKey, Alloc>:: removeNode(NodePointer prem){ size_type index = bucketIndex(prem->hash_, bucket_count_); NodePointer pn = bucket_list_[index]; assert(pn); for (; pn->next_ != prem; pn = pn->next_) ; if (pn == static_cast<NodePointer>(&bucket_start_node_) || bucketIndex(pn->hash_, bucket_count_) != index){ if (prem->next_ == nullptr || bucketIndex(prem->next_->hash_, bucket_count_) != index) bucket_list_[index] = nullptr; } if (prem->next_ != nullptr) { size_type after_index = bucketIndex(prem->next_->hash_, bucket_count_); if (after_index != index) bucket_list_[after_index] = pn; } pn->next_ = prem->next_; --size_; prem->next_ = nullptr; delete prem; }
void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId) { int which = timerId & 0x00ffffff; int bucket = bucketOffset(which); int at = bucketIndex(bucket, which); int *b = timerIds[bucket]; int freeId, newTimerId; do { freeId = nextFreeTimerId; b[at] = freeId & 0x00ffffff; newTimerId = prepareNewValueWithSerialNumber(freeId, timerId); } while (!nextFreeTimerId.testAndSetRelease(freeId, newTimerId)); }
// Releasing a timer ID requires putting the current ID back in the vector; // we do it by setting: // X[timerId] = nextFreeTimerId; // then we update nextFreeTimerId to the timer we've just released // // The extra code in allocateTimerId and releaseTimerId are ABA prevention // and bucket memory. The buckets are simply to make sure we allocate only // the necessary number of timers. See above. // // ABA prevention simply adds a value to 7 of the top 8 bits when resetting // nextFreeTimerId. void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId) { int which = timerId & TimerIdMask; int bucket = bucketOffset(which); int at = bucketIndex(bucket, which); int *b = timerIds[bucket]; Q_ASSERT_X(timerId == -b[at], "QAbstractEventDispatcher::releaseTimerId", "Internal error: timer ID not found"); int freeId, newTimerId; do { freeId = nextFreeTimerId;//.loadAcquire(); // ### FIXME Proper memory ordering semantics b[at] = freeId & TimerIdMask; newTimerId = prepareNewValueWithSerialNumber(freeId, timerId); } while (!nextFreeTimerId.testAndSetRelease(freeId, newTimerId)); }
void Hash_remove(Hash *self, const void *key) { size_t i = bucketIndex(self, key); HashNode *node = self->buckets[i]; if(node){ if(self->cmp(key, node->key) == 0){ clearBucket(self, i); } else { while(node->next){ if(self->cmp(key, node->next->key) == 0){ squeezeNext(node); self->entryCount--; return; } } } } }
void half_free( void * pointer_void ) { U8* pointer = (U8*) pointer_void; U8 bucket; U8 bucket2; U16 v_addr; // The working block (which will contain all merged blocks in the end) block_t block; // Neighboring block block_t block2; // Go left towards beginning of header block pointer -= 4; // Find if block is in our memory pool if ((int)pointer - (int)memory < 0 || (int)pointer - (int)memory >= 32768) { // We're not doing this return; } v_addr = ((int)pointer - (int)memory)/BLOCK_BYTES; // Get block header info getBlockHeader(&block, v_addr); // If there is a free block to the left: if ( (block.v_addr != 0) && !isAllocated(block.prev_mem) ) { // Get left block header info getBlockHeader(&block2, block.prev_mem); // Add left block size to working block size block.size += block2.size; // Working block's address takes left block's address block.v_addr = block.prev_mem; // Working block's prev_mem takes left block's prev_mem block.prev_mem = block2.prev_mem; // block.next.prev := block.v_addr (with _mem) setPrevMem(block.next_mem, block.v_addr); // Remove left block from its bucket bucket2 = bucketIndex(block2.size); if ( block2.v_addr == ramses[bucket2] ){ clearOccupancy(bucket2); } else { setPrevBuck(block2.next_buck, block2.prev_buck); // block2.next.prev := block2.prev (with _buck) setNextBuck(block2.prev_buck, block2.next_buck); // block2.prev.next := block2.next (with _buck) // if bucket.head == block2 if( ramses[bucket2] == block2.v_addr){ // bucket.head := block2.next (with _buck) ramses[bucket2] = block2.next_buck; } } } // If there is a free block to the right: if ( (block.next_mem != 0) && !isAllocated(block.next_mem) ) { // Get right block info getBlockHeader(&block2, block.next_mem); // Add right block size to working block size block.size += block2.size; // Set working block's next_mem to right block's next_mem block.next_mem = block2.next_mem; // block.next.prev := block.v_addr (with _mem) setPrevMem(block.next_mem, block.v_addr); // Remove right block from its bucket bucket2 = bucketIndex(block2.size); if ( block2.v_addr == ramses[bucket2] ){ clearOccupancy(bucket2); } else { // block2.next.prev := block2.prev (with _buck) setPrevBuck(block2.next_buck, block2.prev_buck); // block2.prev.next := block2.next (with _buck) setNextBuck(block2.prev_buck, block2.next_buck); // if bucket.head == block2 if( ramses[bucket2] == block2.v_addr){ // bucket.head := block2.next (with _buck) ramses[bucket2] = block2.next_buck; } } } // Find bucket for working block bucket = bucketIndex(block.size); // If bucket non-empty, insert working block into bucket by appending to existing head // Otherwise, link working block to itself & set as head of bucket if (isOccupied(bucket)) { // block.next := ramses[bucket].next (with _buck) block.next_buck = getNextBuck(ramses[bucket]); // block.prev := ramses[bucket].v_addr (with _buck) block.prev_buck = ramses[bucket]; // ramses[bucket].next.prev := block.v_addr (with _buck) setPrevBuck(getNextBuck(ramses[bucket]), block.v_addr); // ramses[bucket].next := block.v_addr (with _buck) setNextBuck(ramses[bucket], block.v_addr); } else { // Link working block to itself block.next_buck = block.prev_buck = block.v_addr; // Set working block as head of its bucket ramses[bucket] = block.v_addr; setOccupancy(bucket); } // Save working block memory[block.v_addr*BLOCK_SIZE] = (block.prev_mem << 22) | (block.next_mem << 12) | ((block.size-1) << 2); memory[block.v_addr*BLOCK_SIZE + 1] = (block.prev_buck << 22) | (block.next_buck << 12); return; }
void* half_alloc( U32 bytes ){ block_t block; block_t newblock; U8 bucket; U8 newbucket; U8 i; U16 v_addr; // Request 4 more bytes for the header part bytes += 4; // Find a block in the smallest possible non-empty bucket i = bucketIndex( (bytes - 1) >> 5 ) + 1; // Calculate smallest bucket that could fit i += __clz(__rbit(bucketOccupancy >> i)); // Get next non-empty bucket // Stop if we don't have blocks large enough anymore :( if (i > 10) { return NULL; } // Store bucket info safely bucket = i; v_addr = ramses[i]; // Get block header info getBlockHeader(&block, v_addr); // Remove block from bucket linked list if (block.v_addr == block.next_buck) { // Current block is alone in bucket clearOccupancy(bucket); } else { // Current block is not alone in bucket // block.next.prev := block.prev (with _buck) setPrevBuck(block.next_buck, block.prev_buck); // block.prev.next := block.next (with _buck) setNextBuck(block.prev_buck, block.next_buck); // Change linked list buckethead (http://www.youtube.com/watch?v=lkeXE6FOf6s) ramses[bucket] = block.next_buck; } // Mark block as allocated memory[block.v_addr*BLOCK_SIZE] |= 0x0002; if ((block.size*BLOCK_BYTES - bytes >= 32)) { // Block needs to be split into 2 blocks // (block will be returned, newblock stays in free memory) // Set new block size (block.size - ceil(bytes/BLOCK_BYTES)) newblock.size = block.size - ((bytes-1)/BLOCK_BYTES + 1); // Reduce block size block.size -= newblock.size; // Locate new block newblock.v_addr = block.v_addr + block.size; // Put new block into memory linked list newblock.prev_mem = block.v_addr ; newblock.next_mem = block.next_mem ; // block.next.prev := newblock.v_addr (with _mem) setPrevMem(block.next_mem, newblock.v_addr); block.next_mem = newblock.v_addr ; // Save new next_mem & size to current block // (Do not save block.prev_mem! Could overwrite critical data.) memory[block.v_addr*BLOCK_SIZE] = memory[block.v_addr*BLOCK_SIZE] & ~(LAST10 << 12 | LAST10 << 2) | (block.next_mem << 12) | ((block.size-1) << 2); // Find bucket number for new block newbucket = bucketIndex(newblock.size); // If applicable, append new block to existing head of bucket if (isOccupied(newbucket)) { // newblock.next := ramses[newbucket].next (with _buck) newblock.next_buck = getNextBuck(ramses[newbucket]); // newblock.prev := ramses[newbucket].v_addr (with _buck) newblock.prev_buck = ramses[newbucket]; // ramses[newbucket].next.prev := newblock.v_addr (with _buck) setPrevBuck(getNextBuck(ramses[newbucket]), newblock.v_addr); // ramses[newbucket].next := newblock.v_addr (with _buck) setNextBuck(ramses[newbucket], newblock.v_addr); } else { // Link new block to itself newblock.next_buck = newblock.prev_buck = newblock.v_addr; // Set new block as head of its bucket ramses[newbucket] = newblock.v_addr; setOccupancy(newbucket); } // Save new block & set as unallocated memory[newblock.v_addr*BLOCK_SIZE] = (newblock.prev_mem << 22) | (newblock.next_mem << 12) | ((newblock.size-1) << 2); memory[newblock.v_addr*BLOCK_SIZE+1] = (newblock.prev_buck << 22) | (newblock.next_buck << 12); } // Return pointer to current block return (void*)(memory + block.v_addr*BLOCK_SIZE + 1); }
static HashNode *hookBucket(const Hash *self, const void *key) { assume_ptr(self); size_t index = bucketIndex(self, key); return self->buckets[index]; }
size_type HashMap<Key, T, Hasher, EqualKey, Alloc>:: bucket(const Key& k) const{ std::size_t h = hash_function()(k); return bucketIndex(h, bucket_count_); }