// unordered_fast_key_table_internal.h
/* Copyright 2012 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
#pragma once
#include "mongo/util/unordered_fast_key_table.h"
namespace mongo {
template
inline int UnorderedFastKeyTable::Area::find(const HashedKey& key,
int* firstEmpty) const {
dassert(capacity()); // Caller must special-case empty tables.
dassert(!firstEmpty || *firstEmpty == -1); // Caller must initialize *firstEmpty.
unsigned probe = 0;
do {
unsigned pos = (key.hash() + probe) & _hashMask;
if (!_entries[pos].isUsed()) {
// space is empty
if (firstEmpty && *firstEmpty == -1)
*firstEmpty = pos;
if (!_entries[pos].wasEverUsed())
return -1;
continue;
}
if (_entries[pos].getCurHash() != key.hash()) {
// space has something else
continue;
}
if (!Traits::equals(key.key(), Traits::toLookup(_entries[pos].getData().first))) {
// hashes match
// strings are not equals
continue;
}
// hashes and strings are equal
// yay!
return pos;
} while (++probe < _maxProbe);
return -1;
}
template
inline bool UnorderedFastKeyTable::Area::transfer(Area* newArea) const {
for (auto&& entry : *this) {
if (!entry.isUsed())
continue;
int firstEmpty = -1;
int loc = newArea->find(
HashedKey(Traits::toLookup(entry.getData().first), entry.getCurHash()), &firstEmpty);
verify(loc == -1);
if (firstEmpty < 0) {
return false;
}
newArea->_entries[firstEmpty] = entry;
}
return true;
}
template
inline UnorderedFastKeyTable::UnorderedFastKeyTable(
std::initializer_list> entries)
: UnorderedFastKeyTable() {
for (auto&& entry : entries) {
// Only insert the entry if the key is not equivalent to the key of any other element
// already in the table.
auto key = HashedKey(entry.first);
if (find(key) == end()) {
get(key) = entry.second;
}
}
}
template
inline V& UnorderedFastKeyTable::get(const HashedKey& key) {
return try_emplace(key).first->second;
}
template
inline size_t UnorderedFastKeyTable::erase(const HashedKey& key) {
if (_size == 0)
return 0; // Nothing to delete.
int pos = _area.find(key, nullptr);
if (pos < 0)
return 0;
--_size;
_area._entries[pos].unUse();
return 1;
}
template
void UnorderedFastKeyTable::erase(const_iterator it) {
dassert(it._position >= 0);
dassert(it._area == &_area);
--_size;
_area._entries[it._position].unUse();
}
template
template
inline auto UnorderedFastKeyTable::try_emplace(const HashedKey& key,
Args&&... args)
-> std::pair {
if (!_area._entries) {
// This is the first insert ever. Need to allocate initial space.
dassert(_area.capacity() == 0);
_grow();
}
for (int numGrowTries = 0; numGrowTries < 5; numGrowTries++) {
int firstEmpty = -1;
int pos = _area.find(key, &firstEmpty);
if (pos >= 0) {
// This is only possible the first pass through the loop, since you're allocating space
// for a new element after that.
dassert(numGrowTries == 0);
return {iterator(&_area, pos), false};
}
// key not in map
// need to add
if (firstEmpty >= 0) {
_size++;
_area._entries[firstEmpty].emplaceData(key, std::forward(args)...);
return {iterator(&_area, firstEmpty), true};
}
// no space left in map
_grow();
}
msgasserted(16471, "UnorderedFastKeyTable couldn't add entry after growing many times");
}
template
inline void UnorderedFastKeyTable::_grow() {
unsigned capacity = _area.capacity();
for (int numGrowTries = 0; numGrowTries < 5; numGrowTries++) {
if (capacity == 0) {
const unsigned kDefaultStartingCapacity = 16;
capacity = kDefaultStartingCapacity;
} else {
capacity *= 2;
}
const double kMaxProbeRatio = 0.05;
unsigned maxProbes = (capacity * kMaxProbeRatio) + 1; // Round up
Area newArea(capacity, maxProbes);
bool success = _area.transfer(&newArea);
if (!success) {
continue;
}
_area.swap(&newArea);
return;
}
msgasserted(16845, "UnorderedFastKeyTable::_grow couldn't add entry after growing many times");
}
}