2014-03-17 02:01:03 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#include <malloc.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
#include <boost/noncopyable.hpp>
|
|
|
|
|
|
|
|
|
|
#include <Yandex/likely.h>
|
|
|
|
|
|
|
|
|
|
#include <stats/IntHash.h>
|
|
|
|
|
|
2014-12-30 11:27:58 +00:00
|
|
|
|
#include <DB/Core/Defines.h>
|
2014-03-17 02:01:03 +00:00
|
|
|
|
#include <DB/Core/Types.h>
|
|
|
|
|
#include <DB/Core/Exception.h>
|
|
|
|
|
#include <DB/Core/ErrorCodes.h>
|
|
|
|
|
|
|
|
|
|
#include <DB/IO/WriteBuffer.h>
|
|
|
|
|
#include <DB/IO/WriteHelpers.h>
|
|
|
|
|
#include <DB/IO/ReadBuffer.h>
|
|
|
|
|
#include <DB/IO/ReadHelpers.h>
|
|
|
|
|
#include <DB/IO/VarInt.h>
|
|
|
|
|
|
|
|
|
|
#include <DB/Common/HashTable/HashTableAllocator.h>
|
|
|
|
|
|
|
|
|
|
#ifdef DBMS_HASH_MAP_DEBUG_RESIZES
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <statdaemons/Stopwatch.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
/** Состояние хэш-таблицы, которое влияет на свойства её ячеек.
|
|
|
|
|
* Используется в качестве параметра шаблона.
|
|
|
|
|
* Например, существует реализация мгновенно-очищаемой хэш-таблицы - ClearableHashMap.
|
|
|
|
|
* Для неё, в каждой ячейке хранится номер версии, и в самой хэш-таблице - текущая версия.
|
|
|
|
|
* При очистке, просто увеличивается текущая версия; все ячейки с несовпадающей версией считаются пустыми.
|
|
|
|
|
* Другой пример: для приближённого рассчёта количества уникальных посетителей, есть хэш-таблица UniquesHashSet.
|
|
|
|
|
* В ней имеется понятие "степень". При каждом переполнении, ячейки с ключами, не делящимися на соответствующую степень двух, удаляются.
|
|
|
|
|
*/
|
|
|
|
|
struct HashTableNoState
|
|
|
|
|
{
|
|
|
|
|
/// Сериализация, в бинарном и текстовом виде.
|
|
|
|
|
void write(DB::WriteBuffer & wb) const {}
|
|
|
|
|
void writeText(DB::WriteBuffer & wb) const {}
|
|
|
|
|
|
|
|
|
|
/// Десериализация, в бинарном и текстовом виде.
|
|
|
|
|
void read(DB::ReadBuffer & rb) {}
|
|
|
|
|
void readText(DB::ReadBuffer & rb) {}
|
|
|
|
|
};
|
|
|
|
|
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
2014-04-29 00:28:18 +00:00
|
|
|
|
/// Эти функции могут быть перегружены для пользовательских типов.
|
|
|
|
|
namespace ZeroTraits
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
bool check(const T x) { return x == 0; }
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
void set(T & x) { x = 0; }
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2014-03-17 02:01:03 +00:00
|
|
|
|
/** Compile-time интерфейс ячейки хэш-таблицы.
|
|
|
|
|
* Разные ячейки используются для реализации разных хэш-таблиц.
|
|
|
|
|
* Ячейка должна содержать ключ.
|
2014-04-28 01:48:24 +00:00
|
|
|
|
* Также может содержать значение и произвольные дополнительные данные
|
|
|
|
|
* (пример: запомненное значение хэш-функции; номер версии для ClearableHashMap).
|
2014-03-17 02:01:03 +00:00
|
|
|
|
*/
|
2014-04-28 02:47:56 +00:00
|
|
|
|
template <typename Key, typename Hash, typename TState = HashTableNoState>
|
2014-03-17 02:01:03 +00:00
|
|
|
|
struct HashTableCell
|
|
|
|
|
{
|
2014-04-28 02:47:56 +00:00
|
|
|
|
typedef TState State;
|
2014-04-28 01:48:24 +00:00
|
|
|
|
|
2014-03-17 02:01:03 +00:00
|
|
|
|
typedef Key value_type;
|
|
|
|
|
Key key;
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
HashTableCell() {}
|
|
|
|
|
|
2014-03-17 02:01:03 +00:00
|
|
|
|
/// Создать ячейку с заданным ключём / ключём и значением.
|
2014-04-28 01:48:24 +00:00
|
|
|
|
HashTableCell(const Key & key_, const State & state) : key(key_) {}
|
|
|
|
|
/// HashTableCell(const value_type & value_, const State & state) : key(value_) {}
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
/// Получить то, что будет value_type контейнера.
|
|
|
|
|
value_type & getValue() { return key; }
|
|
|
|
|
const value_type & getValue() const { return key; }
|
|
|
|
|
|
|
|
|
|
/// Получить ключ.
|
|
|
|
|
static Key & getKey(value_type & value) { return value; }
|
|
|
|
|
static const Key & getKey(const value_type & value) { return value; }
|
|
|
|
|
|
|
|
|
|
/// Равны ли ключи у ячеек.
|
|
|
|
|
bool keyEquals(const Key & key_) const { return key == key_; }
|
2014-11-09 08:39:18 +00:00
|
|
|
|
bool keyEquals(const Key & key_, size_t hash_) const { return key == key_; }
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
/// Если ячейка умеет запоминать в себе значение хэш-функции, то запомнить его.
|
|
|
|
|
void setHash(size_t hash_value) {}
|
|
|
|
|
|
|
|
|
|
/// Если ячейка умеет запоминать в себе значение хэш-функции, то вернуть запомненное значение.
|
|
|
|
|
/// Оно должно быть хотя бы один раз вычислено до этого.
|
|
|
|
|
/// Если запоминание значения хэш-функции не предусмотрено, то просто вычислить хэш.
|
|
|
|
|
size_t getHash(const Hash & hash) const { return hash(key); }
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
/// Является ли ключ нулевым. В основном буфере, ячейки с нулевым ключём, считаются пустыми.
|
|
|
|
|
/// Если нулевые ключи могут быть вставлены в таблицу, то ячейка для нулевого ключа хранится отдельно, не в основном буфере.
|
2014-03-17 02:01:03 +00:00
|
|
|
|
/// Нулевые ключи должны быть такими, что занулённый кусок памяти представляет собой нулевой ключ.
|
2014-04-28 01:48:24 +00:00
|
|
|
|
bool isZero(const State & state) const { return isZero(key, state); }
|
2014-04-29 00:28:18 +00:00
|
|
|
|
static bool isZero(const Key & key, const State & state) { return ZeroTraits::check(key); }
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
/// Установить значение ключа в ноль.
|
2014-04-29 00:28:18 +00:00
|
|
|
|
void setZero() { ZeroTraits::set(key); }
|
2014-04-28 01:48:24 +00:00
|
|
|
|
|
|
|
|
|
/// Нужно ли хранить нулевой ключ отдельно (то есть, могут ли в хэш-таблицу вставить нулевой ключ).
|
|
|
|
|
static constexpr bool need_zero_value_storage = true;
|
|
|
|
|
|
|
|
|
|
/// Является ли ячейка удалённой.
|
|
|
|
|
bool isDeleted() const { return false; }
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
2014-03-20 23:14:07 +00:00
|
|
|
|
/// Установить отображаемое значение, если есть (для HashMap), в соответствующиее из value.
|
2014-03-17 02:01:03 +00:00
|
|
|
|
void setMapped(const value_type & value) {}
|
|
|
|
|
|
|
|
|
|
/// Сериализация, в бинарном и текстовом виде.
|
|
|
|
|
void write(DB::WriteBuffer & wb) const { DB::writeBinary(key, wb); }
|
|
|
|
|
void writeText(DB::WriteBuffer & wb) const { DB::writeDoubleQuoted(key, wb); }
|
|
|
|
|
|
|
|
|
|
/// Десериализация, в бинарном и текстовом виде.
|
2014-04-28 01:48:24 +00:00
|
|
|
|
void read(DB::ReadBuffer & rb) { DB::readBinary(key, rb); }
|
|
|
|
|
void readText(DB::ReadBuffer & rb) { DB::writeDoubleQuoted(key, rb); }
|
2014-03-17 02:01:03 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Определяет размер хэш-таблицы, а также когда и во сколько раз её надо ресайзить.
|
|
|
|
|
*/
|
2015-01-11 02:30:33 +00:00
|
|
|
|
template <size_t initial_size_degree = 8>
|
2014-03-17 02:01:03 +00:00
|
|
|
|
struct HashTableGrower
|
|
|
|
|
{
|
|
|
|
|
/// Состояние этой структуры достаточно, чтобы получить размер буфера хэш-таблицы.
|
|
|
|
|
|
|
|
|
|
UInt8 size_degree = initial_size_degree;
|
|
|
|
|
|
|
|
|
|
/// Размер хэш-таблицы в ячейках.
|
|
|
|
|
size_t bufSize() const { return 1 << size_degree; }
|
|
|
|
|
|
|
|
|
|
size_t maxFill() const { return 1 << (size_degree - 1); }
|
|
|
|
|
size_t mask() const { return bufSize() - 1; }
|
|
|
|
|
|
|
|
|
|
/// Из значения хэш-функции получить номер ячейки в хэш-таблице.
|
|
|
|
|
size_t place(size_t x) const { return x & mask(); }
|
|
|
|
|
|
|
|
|
|
/// Следующая ячейка в цепочке разрешения коллизий.
|
|
|
|
|
size_t next(size_t pos) const { ++pos; return pos & mask(); }
|
|
|
|
|
|
|
|
|
|
/// Является ли хэш-таблица достаточно заполненной. Нужно увеличить размер хэш-таблицы, или удалить из неё что-нибудь ненужное.
|
|
|
|
|
bool overflow(size_t elems) const { return elems > maxFill(); }
|
|
|
|
|
|
|
|
|
|
/// Увеличить размер хэш-таблицы.
|
|
|
|
|
void increaseSize()
|
|
|
|
|
{
|
|
|
|
|
size_degree += size_degree >= 23 ? 1 : 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Установить размер буфера по количеству элементов хэш-таблицы. Используется при десериализации хэш-таблицы.
|
|
|
|
|
void set(size_t num_elems)
|
|
|
|
|
{
|
|
|
|
|
size_degree = num_elems <= 1
|
|
|
|
|
? initial_size_degree
|
|
|
|
|
: ((initial_size_degree > static_cast<size_t>(log2(num_elems - 1)) + 2)
|
|
|
|
|
? initial_size_degree
|
|
|
|
|
: (static_cast<size_t>(log2(num_elems - 1)) + 2));
|
|
|
|
|
}
|
2014-05-03 01:45:34 +00:00
|
|
|
|
|
|
|
|
|
void setBufSize(size_t buf_size_)
|
|
|
|
|
{
|
|
|
|
|
size_degree = static_cast<size_t>(log2(buf_size_ - 1) + 1);
|
|
|
|
|
}
|
2014-03-17 02:01:03 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
/** Если нужно хранить нулевой ключ отдельно - место для его хранения. */
|
|
|
|
|
template <bool need_zero_value_storage, typename Cell>
|
|
|
|
|
struct ZeroValueStorage;
|
|
|
|
|
|
|
|
|
|
template <typename Cell>
|
|
|
|
|
struct ZeroValueStorage<true, Cell>
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
bool has_zero = false;
|
2014-05-10 20:47:12 +00:00
|
|
|
|
char zero_value_storage[sizeof(Cell)] __attribute__((__aligned__(__alignof__(Cell)))); /// Кусок памяти для элемента с ключём 0.
|
2014-04-28 01:48:24 +00:00
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
bool hasZero() const { return has_zero; }
|
|
|
|
|
void setHasZero() { has_zero = true; }
|
|
|
|
|
void clearHasZero() { has_zero = false; }
|
|
|
|
|
|
|
|
|
|
Cell * zeroValue() { return reinterpret_cast<Cell*>(zero_value_storage); }
|
|
|
|
|
const Cell * zeroValue() const { return reinterpret_cast<const Cell*>(zero_value_storage); }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename Cell>
|
|
|
|
|
struct ZeroValueStorage<false, Cell>
|
|
|
|
|
{
|
|
|
|
|
bool hasZero() const { return false; }
|
|
|
|
|
void setHasZero() { throw DB::Exception("HashTable: logical error", DB::ErrorCodes::LOGICAL_ERROR); }
|
|
|
|
|
void clearHasZero() {}
|
|
|
|
|
|
|
|
|
|
Cell * zeroValue() { return nullptr; }
|
|
|
|
|
const Cell * zeroValue() const { return nullptr; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2014-03-17 02:01:03 +00:00
|
|
|
|
template
|
|
|
|
|
<
|
|
|
|
|
typename Key,
|
|
|
|
|
typename Cell,
|
|
|
|
|
typename Hash,
|
|
|
|
|
typename Grower,
|
|
|
|
|
typename Allocator
|
|
|
|
|
>
|
2014-04-28 01:48:24 +00:00
|
|
|
|
class HashTable :
|
|
|
|
|
private boost::noncopyable,
|
|
|
|
|
protected Hash,
|
|
|
|
|
protected Allocator,
|
|
|
|
|
protected Cell::State,
|
|
|
|
|
protected ZeroValueStorage<Cell::need_zero_value_storage, Cell> /// empty base optimization
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
protected:
|
2014-03-17 02:01:03 +00:00
|
|
|
|
friend class const_iterator;
|
|
|
|
|
friend class iterator;
|
|
|
|
|
|
2014-12-30 12:58:02 +00:00
|
|
|
|
template <typename, typename, typename, typename, typename, typename, size_t>
|
|
|
|
|
friend class TwoLevelHashTable;
|
|
|
|
|
|
2014-03-17 02:01:03 +00:00
|
|
|
|
typedef size_t HashValue;
|
|
|
|
|
typedef HashTable<Key, Cell, Hash, Grower, Allocator> Self;
|
2014-05-03 00:08:35 +00:00
|
|
|
|
typedef Cell cell_type;
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
size_t m_size = 0; /// Количество элементов
|
2014-03-17 02:01:03 +00:00
|
|
|
|
Cell * buf; /// Кусок памяти для всех элементов кроме элемента с ключём 0.
|
|
|
|
|
Grower grower;
|
|
|
|
|
|
|
|
|
|
#ifdef DBMS_HASH_MAP_COUNT_COLLISIONS
|
2014-12-25 00:19:29 +00:00
|
|
|
|
mutable size_t collisions = 0;
|
2014-03-17 02:01:03 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/// Найти ячейку с тем же ключём или пустую ячейку, начиная с заданного места и далее по цепочке разрешения коллизий.
|
2014-12-30 11:27:58 +00:00
|
|
|
|
size_t ALWAYS_INLINE findCell(const Key & x, size_t hash_value, size_t place_value) const
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
2014-11-09 08:39:18 +00:00
|
|
|
|
while (!buf[place_value].isZero(*this) && !buf[place_value].keyEquals(x, hash_value))
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
|
|
|
|
place_value = grower.next(place_value);
|
|
|
|
|
#ifdef DBMS_HASH_MAP_COUNT_COLLISIONS
|
|
|
|
|
++collisions;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return place_value;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-30 11:27:58 +00:00
|
|
|
|
/// Найти пустую ячейку, начиная с заданного места и далее по цепочке разрешения коллизий.
|
|
|
|
|
size_t ALWAYS_INLINE findEmptyCell(const Key & x, size_t hash_value, size_t place_value) const
|
|
|
|
|
{
|
|
|
|
|
while (!buf[place_value].isZero(*this))
|
|
|
|
|
{
|
|
|
|
|
place_value = grower.next(place_value);
|
|
|
|
|
#ifdef DBMS_HASH_MAP_COUNT_COLLISIONS
|
|
|
|
|
++collisions;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return place_value;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-22 00:22:48 +00:00
|
|
|
|
void alloc(const Grower & new_grower)
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
2014-05-22 00:22:48 +00:00
|
|
|
|
buf = reinterpret_cast<Cell *>(Allocator::alloc(new_grower.bufSize() * sizeof(Cell)));
|
|
|
|
|
grower = new_grower;
|
2014-03-17 02:01:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void free()
|
|
|
|
|
{
|
2014-05-22 00:22:48 +00:00
|
|
|
|
Allocator::free(buf, getBufferSizeInBytes());
|
2014-03-17 02:01:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Увеличить размер буфера.
|
2014-05-03 01:45:34 +00:00
|
|
|
|
void resize(size_t for_num_elems = 0, size_t for_buf_size = 0)
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
|
|
|
|
#ifdef DBMS_HASH_MAP_DEBUG_RESIZES
|
|
|
|
|
Stopwatch watch;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
size_t old_size = grower.bufSize();
|
|
|
|
|
|
2014-05-22 00:22:48 +00:00
|
|
|
|
/** Чтобы в случае исключения, объект остался в корректном состоянии,
|
|
|
|
|
* изменение переменной grower (определяющией размер буфера хэш-таблицы)
|
|
|
|
|
* откладываем на момент после реального изменения буфера.
|
|
|
|
|
* Временная переменная new_grower используется, чтобы определить новый размер.
|
|
|
|
|
*/
|
|
|
|
|
Grower new_grower = grower;
|
|
|
|
|
|
2014-03-17 02:01:03 +00:00
|
|
|
|
if (for_num_elems)
|
|
|
|
|
{
|
2014-05-22 00:22:48 +00:00
|
|
|
|
new_grower.set(for_num_elems);
|
|
|
|
|
if (new_grower.bufSize() <= old_size)
|
2014-03-17 02:01:03 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2014-05-03 01:45:34 +00:00
|
|
|
|
else if (for_buf_size)
|
|
|
|
|
{
|
2014-05-22 00:22:48 +00:00
|
|
|
|
new_grower.setBufSize(for_buf_size);
|
|
|
|
|
if (new_grower.bufSize() <= old_size)
|
2014-05-03 01:45:34 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2014-03-17 02:01:03 +00:00
|
|
|
|
else
|
2014-05-22 00:22:48 +00:00
|
|
|
|
new_grower.increaseSize();
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
/// Расширим пространство.
|
2014-05-22 00:22:48 +00:00
|
|
|
|
buf = reinterpret_cast<Cell *>(Allocator::realloc(buf, getBufferSizeInBytes(), new_grower.bufSize() * sizeof(Cell)));
|
|
|
|
|
grower = new_grower;
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
/** Теперь некоторые элементы может потребоваться переместить на новое место.
|
|
|
|
|
* Элемент может остаться на месте, или переместиться в новое место "справа",
|
|
|
|
|
* или переместиться левее по цепочке разрешения коллизий, из-за того, что элементы левее него были перемещены в новое место "справа".
|
|
|
|
|
*/
|
2014-04-28 01:48:24 +00:00
|
|
|
|
size_t i = 0;
|
|
|
|
|
for (; i < old_size; ++i)
|
|
|
|
|
if (!buf[i].isZero(*this) && !buf[i].isDeleted())
|
2014-03-17 02:01:03 +00:00
|
|
|
|
reinsert(buf[i]);
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
/** Также имеется особый случай:
|
|
|
|
|
* если элемент должен был быть в конце старого буфера, [ x]
|
|
|
|
|
* но находится в начале из-за цепочки разрешения коллизий, [o x]
|
|
|
|
|
* то после ресайза, он сначала снова окажется не на своём месте, [ xo ]
|
|
|
|
|
* и для того, чтобы перенести его куда надо,
|
|
|
|
|
* надо будет после переноса всех элементов из старой половинки [ o x ]
|
|
|
|
|
* обработать ещё хвостик из цепочки разрешения коллизий сразу после неё [ o x ]
|
|
|
|
|
*/
|
|
|
|
|
for (; !buf[i].isZero(*this) && !buf[i].isDeleted(); ++i)
|
|
|
|
|
reinsert(buf[i]);
|
|
|
|
|
|
2014-03-17 02:01:03 +00:00
|
|
|
|
#ifdef DBMS_HASH_MAP_DEBUG_RESIZES
|
|
|
|
|
watch.stop();
|
|
|
|
|
std::cerr << std::fixed << std::setprecision(3)
|
|
|
|
|
<< "Resize from " << old_size << " to " << grower.bufSize() << " took " << watch.elapsedSeconds() << " sec."
|
|
|
|
|
<< std::endl;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Вставить в новый буфер значение, которое было в старом буфере.
|
|
|
|
|
* Используется при увеличении размера буфера.
|
|
|
|
|
*/
|
|
|
|
|
void reinsert(Cell & x)
|
|
|
|
|
{
|
2014-11-09 08:39:18 +00:00
|
|
|
|
size_t hash_value = x.getHash(*this);
|
|
|
|
|
size_t place_value = grower.place(hash_value);
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
/// Если элемент на своём месте.
|
|
|
|
|
if (&x == &buf[place_value])
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/// Вычисление нового места, с учётом цепочки разрешения коллизий.
|
2014-11-09 08:39:18 +00:00
|
|
|
|
place_value = findCell(Cell::getKey(x.getValue()), hash_value, place_value);
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
/// Если элемент остался на своём месте в старой цепочке разрешения коллизий.
|
2014-05-02 19:25:17 +00:00
|
|
|
|
if (!buf[place_value].isZero(*this))
|
2014-03-17 02:01:03 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/// Копирование на новое место и зануление старого.
|
|
|
|
|
memcpy(&buf[place_value], &x, sizeof(x));
|
|
|
|
|
x.setZero();
|
|
|
|
|
|
|
|
|
|
/// Потом на старое место могут переместиться элементы, которые раньше были в коллизии с этим.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
typedef Key key_type;
|
|
|
|
|
typedef typename Cell::value_type value_type;
|
|
|
|
|
|
2014-05-22 00:22:48 +00:00
|
|
|
|
size_t hash(const Key & x) const { return Hash::operator()(x); }
|
|
|
|
|
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
HashTable()
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (Cell::need_zero_value_storage)
|
|
|
|
|
this->zeroValue()->setZero();
|
2014-05-22 00:22:48 +00:00
|
|
|
|
alloc(grower);
|
2014-12-25 00:19:29 +00:00
|
|
|
|
}
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
2014-12-25 00:19:29 +00:00
|
|
|
|
HashTable(size_t reserve_for_num_elements)
|
|
|
|
|
{
|
|
|
|
|
if (Cell::need_zero_value_storage)
|
|
|
|
|
this->zeroValue()->setZero();
|
|
|
|
|
grower.set(reserve_for_num_elements);
|
|
|
|
|
alloc(grower);
|
2014-03-17 02:01:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~HashTable()
|
|
|
|
|
{
|
|
|
|
|
if (!__has_trivial_destructor(Cell))
|
|
|
|
|
for (iterator it = begin(); it != end(); ++it)
|
|
|
|
|
it.ptr->~Cell();
|
|
|
|
|
|
|
|
|
|
free();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class iterator
|
|
|
|
|
{
|
|
|
|
|
Self * container;
|
|
|
|
|
Cell * ptr;
|
|
|
|
|
|
|
|
|
|
friend class HashTable;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
iterator() {}
|
2014-05-02 19:25:17 +00:00
|
|
|
|
iterator(Self * container_, Cell * ptr_) : container(container_), ptr(ptr_) {}
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
bool operator== (const iterator & rhs) const { return ptr == rhs.ptr; }
|
|
|
|
|
bool operator!= (const iterator & rhs) const { return ptr != rhs.ptr; }
|
|
|
|
|
|
|
|
|
|
iterator & operator++()
|
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (unlikely(ptr->isZero(*container)))
|
2014-03-17 02:01:03 +00:00
|
|
|
|
ptr = container->buf;
|
|
|
|
|
else
|
|
|
|
|
++ptr;
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
while (ptr < container->buf + container->grower.bufSize() && ptr->isZero(*container))
|
2014-03-17 02:01:03 +00:00
|
|
|
|
++ptr;
|
|
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value_type & operator* () const { return ptr->getValue(); }
|
|
|
|
|
value_type * operator->() const { return &ptr->getValue(); }
|
2014-12-30 12:58:02 +00:00
|
|
|
|
|
|
|
|
|
Cell * getPtr() const { return ptr; }
|
2014-12-30 20:38:05 +00:00
|
|
|
|
size_t getHash() const { return ptr->getHash(*container); }
|
2014-03-17 02:01:03 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class const_iterator
|
|
|
|
|
{
|
|
|
|
|
const Self * container;
|
|
|
|
|
const Cell * ptr;
|
|
|
|
|
|
|
|
|
|
friend class HashTable;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
const_iterator() {}
|
2014-05-02 19:25:17 +00:00
|
|
|
|
const_iterator(const Self * container_, const Cell * ptr_) : container(container_), ptr(ptr_) {}
|
2014-03-17 02:01:03 +00:00
|
|
|
|
const_iterator(const iterator & rhs) : container(rhs.container), ptr(rhs.ptr) {}
|
|
|
|
|
|
|
|
|
|
bool operator== (const const_iterator & rhs) const { return ptr == rhs.ptr; }
|
|
|
|
|
bool operator!= (const const_iterator & rhs) const { return ptr != rhs.ptr; }
|
|
|
|
|
|
|
|
|
|
const_iterator & operator++()
|
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (unlikely(ptr->isZero(*container)))
|
2014-03-17 02:01:03 +00:00
|
|
|
|
ptr = container->buf;
|
|
|
|
|
else
|
|
|
|
|
++ptr;
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
while (ptr < container->buf + container->grower.bufSize() && ptr->isZero(*container))
|
2014-03-17 02:01:03 +00:00
|
|
|
|
++ptr;
|
|
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const value_type & operator* () const { return ptr->getValue(); }
|
|
|
|
|
const value_type * operator->() const { return &ptr->getValue(); }
|
2014-12-30 12:58:02 +00:00
|
|
|
|
|
|
|
|
|
const Cell * getPtr() const { return ptr; }
|
2014-12-30 20:38:05 +00:00
|
|
|
|
size_t getHash() const { return ptr->getHash(*container); }
|
2014-03-17 02:01:03 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const_iterator begin() const
|
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (this->hasZero())
|
2014-10-29 02:35:16 +00:00
|
|
|
|
return iteratorToZero();
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
const Cell * ptr = buf;
|
2014-04-28 01:48:24 +00:00
|
|
|
|
while (ptr < buf + grower.bufSize() && ptr->isZero(*this))
|
2014-03-17 02:01:03 +00:00
|
|
|
|
++ptr;
|
|
|
|
|
|
|
|
|
|
return const_iterator(this, ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iterator begin()
|
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (this->hasZero())
|
2014-10-29 02:35:16 +00:00
|
|
|
|
return iteratorToZero();
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
Cell * ptr = buf;
|
2014-04-28 01:48:24 +00:00
|
|
|
|
while (ptr < buf + grower.bufSize() && ptr->isZero(*this))
|
2014-03-17 02:01:03 +00:00
|
|
|
|
++ptr;
|
|
|
|
|
|
|
|
|
|
return iterator(this, ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const_iterator end() const { return const_iterator(this, buf + grower.bufSize()); }
|
|
|
|
|
iterator end() { return iterator(this, buf + grower.bufSize()); }
|
|
|
|
|
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
protected:
|
2014-12-26 03:00:51 +00:00
|
|
|
|
const_iterator iteratorTo(const Cell * ptr) const { return const_iterator(this, ptr); }
|
|
|
|
|
iterator iteratorTo(Cell * ptr) { return iterator(this, ptr); }
|
|
|
|
|
const_iterator iteratorToZero() const { return iteratorTo(this->zeroValue()); }
|
|
|
|
|
iterator iteratorToZero() { return iteratorTo(this->zeroValue()); }
|
2014-10-29 02:35:16 +00:00
|
|
|
|
|
|
|
|
|
|
2014-03-17 02:01:03 +00:00
|
|
|
|
/// Если ключ нулевой - вставить его в специальное место и вернуть true.
|
2014-12-30 11:27:58 +00:00
|
|
|
|
bool ALWAYS_INLINE emplaceIfZero(Key x, iterator & it, bool & inserted)
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
2014-05-01 17:26:46 +00:00
|
|
|
|
/// Если утверждается, что нулевой ключ не могут вставить в таблицу.
|
|
|
|
|
if (!Cell::need_zero_value_storage)
|
|
|
|
|
return false;
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (Cell::isZero(x, *this))
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
2014-10-29 02:35:16 +00:00
|
|
|
|
it = iteratorToZero();
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (!this->hasZero())
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
|
|
|
|
++m_size;
|
2014-04-28 01:48:24 +00:00
|
|
|
|
this->setHasZero();
|
2014-10-29 02:35:16 +00:00
|
|
|
|
it.ptr->setHash(hash(x));
|
2014-03-17 02:01:03 +00:00
|
|
|
|
inserted = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
inserted = false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Только для ненулевых ключей. Найти нужное место, вставить туда ключ, если его ещё нет, вернуть итератор на ячейку.
|
2014-12-30 11:27:58 +00:00
|
|
|
|
void ALWAYS_INLINE emplaceNonZero(Key x, iterator & it, bool & inserted, size_t hash_value)
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
2014-11-09 08:39:18 +00:00
|
|
|
|
size_t place_value = findCell(x, hash_value, grower.place(hash_value));
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
it = iterator(this, &buf[place_value]);
|
|
|
|
|
|
2014-05-02 19:25:17 +00:00
|
|
|
|
if (!buf[place_value].isZero(*this))
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
|
|
|
|
inserted = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
new(&buf[place_value]) Cell(x, *this);
|
2014-03-17 02:01:03 +00:00
|
|
|
|
buf[place_value].setHash(hash_value);
|
|
|
|
|
inserted = true;
|
|
|
|
|
++m_size;
|
|
|
|
|
|
|
|
|
|
if (unlikely(grower.overflow(m_size)))
|
|
|
|
|
{
|
|
|
|
|
resize();
|
2014-12-25 21:25:43 +00:00
|
|
|
|
it = find(x, hash_value);
|
2014-03-17 02:01:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
/// Вставить значение. В случае хоть сколько-нибудь сложных значений, лучше используйте функцию emplace.
|
2014-12-30 11:27:58 +00:00
|
|
|
|
std::pair<iterator, bool> ALWAYS_INLINE insert(const value_type & x)
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
|
|
|
|
std::pair<iterator, bool> res;
|
|
|
|
|
|
|
|
|
|
if (!emplaceIfZero(Cell::getKey(x), res.first, res.second))
|
|
|
|
|
emplaceNonZero(Cell::getKey(x), res.first, res.second, hash(Cell::getKey(x)));
|
|
|
|
|
|
|
|
|
|
if (res.second)
|
|
|
|
|
res.first.ptr->setMapped(x);
|
2014-10-29 02:35:16 +00:00
|
|
|
|
|
2014-03-17 02:01:03 +00:00
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Вставить ключ,
|
|
|
|
|
* вернуть итератор на позицию, которую можно использовать для placement new значения,
|
|
|
|
|
* а также флаг - был ли вставлен новый ключ.
|
|
|
|
|
*
|
|
|
|
|
* Вы обязаны сделать placement new значения, если был вставлен новый ключ,
|
|
|
|
|
* так как при уничтожении хэш-таблицы для него будет вызываться деструктор!
|
|
|
|
|
*
|
|
|
|
|
* Пример использования:
|
|
|
|
|
*
|
|
|
|
|
* Map::iterator it;
|
|
|
|
|
* bool inserted;
|
|
|
|
|
* map.emplace(key, it, inserted);
|
|
|
|
|
* if (inserted)
|
|
|
|
|
* new(&it->second) Mapped(value);
|
|
|
|
|
*/
|
2014-12-30 11:27:58 +00:00
|
|
|
|
void ALWAYS_INLINE emplace(Key x, iterator & it, bool & inserted)
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
|
|
|
|
if (!emplaceIfZero(x, it, inserted))
|
|
|
|
|
emplaceNonZero(x, it, inserted, hash(x));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// То же самое, но с заранее вычисленным значением хэш-функции.
|
2014-12-30 11:27:58 +00:00
|
|
|
|
void ALWAYS_INLINE emplace(Key x, iterator & it, bool & inserted, size_t hash_value)
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
|
|
|
|
if (!emplaceIfZero(x, it, inserted))
|
|
|
|
|
emplaceNonZero(x, it, inserted, hash_value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-12-30 11:27:58 +00:00
|
|
|
|
/// Скопировать ячейку из другой хэш-таблицы. Предполагается, что ячейка не нулевая, а также, что такого ключа в таблице ещё не было.
|
2014-12-30 12:58:02 +00:00
|
|
|
|
void ALWAYS_INLINE insertUniqueNonZero(const Cell * cell, size_t hash_value)
|
2014-12-30 11:27:58 +00:00
|
|
|
|
{
|
2014-12-30 12:58:02 +00:00
|
|
|
|
size_t place_value = findEmptyCell(cell->getKey(cell->getValue()), hash_value, grower.place(hash_value));
|
2014-12-30 11:27:58 +00:00
|
|
|
|
|
|
|
|
|
memcpy(&buf[place_value], cell, sizeof(*cell));
|
|
|
|
|
++m_size;
|
|
|
|
|
|
|
|
|
|
if (unlikely(grower.overflow(m_size)))
|
|
|
|
|
resize();
|
2014-12-30 12:58:02 +00:00
|
|
|
|
}
|
2014-12-30 11:27:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
iterator ALWAYS_INLINE find(Key x)
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (Cell::isZero(x, *this))
|
2014-10-29 02:35:16 +00:00
|
|
|
|
return this->hasZero() ? iteratorToZero() : end();
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
2014-11-09 08:39:18 +00:00
|
|
|
|
size_t hash_value = hash(x);
|
|
|
|
|
size_t place_value = findCell(x, hash_value, grower.place(hash_value));
|
2014-04-28 01:48:24 +00:00
|
|
|
|
return !buf[place_value].isZero(*this) ? iterator(this, &buf[place_value]) : end();
|
2014-03-17 02:01:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-12-30 11:27:58 +00:00
|
|
|
|
const_iterator ALWAYS_INLINE find(Key x) const
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (Cell::isZero(x, *this))
|
2014-10-29 02:35:16 +00:00
|
|
|
|
return this->hasZero() ? iteratorToZero() : end();
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
2014-11-09 08:39:18 +00:00
|
|
|
|
size_t hash_value = hash(x);
|
|
|
|
|
size_t place_value = findCell(x, hash_value, grower.place(hash_value));
|
2014-12-25 21:25:43 +00:00
|
|
|
|
return !buf[place_value].isZero(*this) ? const_iterator(this, &buf[place_value]) : end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-12-30 11:27:58 +00:00
|
|
|
|
iterator ALWAYS_INLINE find(Key x, size_t hash_value)
|
2014-12-25 21:25:43 +00:00
|
|
|
|
{
|
|
|
|
|
if (Cell::isZero(x, *this))
|
|
|
|
|
return this->hasZero() ? iteratorToZero() : end();
|
|
|
|
|
|
|
|
|
|
size_t place_value = findCell(x, hash_value, grower.place(hash_value));
|
|
|
|
|
return !buf[place_value].isZero(*this) ? iterator(this, &buf[place_value]) : end();
|
|
|
|
|
}
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
2014-12-25 21:25:43 +00:00
|
|
|
|
|
2014-12-30 11:27:58 +00:00
|
|
|
|
const_iterator ALWAYS_INLINE find(Key x, size_t hash_value) const
|
2014-12-25 21:25:43 +00:00
|
|
|
|
{
|
|
|
|
|
if (Cell::isZero(x, *this))
|
|
|
|
|
return this->hasZero() ? iteratorToZero() : end();
|
|
|
|
|
|
|
|
|
|
size_t place_value = findCell(x, hash_value, grower.place(hash_value));
|
2014-04-28 01:48:24 +00:00
|
|
|
|
return !buf[place_value].isZero(*this) ? const_iterator(this, &buf[place_value]) : end();
|
2014-03-17 02:01:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void write(DB::WriteBuffer & wb) const
|
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
Cell::State::write(wb);
|
2014-03-17 02:01:03 +00:00
|
|
|
|
DB::writeVarUInt(m_size, wb);
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (this->hasZero())
|
|
|
|
|
this->zeroValue()->write(wb);
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < grower.bufSize(); ++i)
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (!buf[i].isZero(*this))
|
2014-03-17 02:01:03 +00:00
|
|
|
|
buf[i].write(wb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void writeText(DB::WriteBuffer & wb) const
|
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
Cell::State::writeText(wb);
|
2014-03-17 02:01:03 +00:00
|
|
|
|
DB::writeText(m_size, wb);
|
|
|
|
|
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (this->hasZero())
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
|
|
|
|
DB::writeChar(',', wb);
|
2014-04-28 01:48:24 +00:00
|
|
|
|
this->zeroValue()->writeText(wb);
|
2014-03-17 02:01:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < grower.bufSize(); ++i)
|
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
if (!buf[i].isZero(*this))
|
2014-03-17 02:01:03 +00:00
|
|
|
|
{
|
|
|
|
|
DB::writeChar(',', wb);
|
|
|
|
|
buf[i].writeText(wb);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void read(DB::ReadBuffer & rb)
|
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
Cell::State::read(rb);
|
|
|
|
|
|
|
|
|
|
this->clearHasZero();
|
2014-03-17 02:01:03 +00:00
|
|
|
|
m_size = 0;
|
|
|
|
|
|
|
|
|
|
size_t new_size = 0;
|
|
|
|
|
DB::readVarUInt(new_size, rb);
|
|
|
|
|
|
|
|
|
|
free();
|
2014-05-22 00:22:48 +00:00
|
|
|
|
Grower new_grower = grower;
|
|
|
|
|
new_grower.set(new_size);
|
|
|
|
|
alloc(new_grower);
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < new_size; ++i)
|
|
|
|
|
{
|
|
|
|
|
Cell x;
|
|
|
|
|
x.read(rb);
|
|
|
|
|
insert(x);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void readText(DB::ReadBuffer & rb)
|
|
|
|
|
{
|
2014-04-28 01:48:24 +00:00
|
|
|
|
Cell::State::readText(rb);
|
|
|
|
|
|
|
|
|
|
this->clearHasZero();
|
2014-03-17 02:01:03 +00:00
|
|
|
|
m_size = 0;
|
|
|
|
|
|
|
|
|
|
size_t new_size = 0;
|
|
|
|
|
DB::readText(new_size, rb);
|
|
|
|
|
|
|
|
|
|
free();
|
2014-05-22 00:22:48 +00:00
|
|
|
|
Grower new_grower = grower;
|
|
|
|
|
new_grower.set(new_size);
|
|
|
|
|
alloc(new_grower);
|
2014-03-17 02:01:03 +00:00
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < new_size; ++i)
|
|
|
|
|
{
|
|
|
|
|
Cell x;
|
|
|
|
|
DB::assertString(",", rb);
|
|
|
|
|
x.readText(rb);
|
|
|
|
|
insert(x);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
size_t size() const
|
|
|
|
|
{
|
|
|
|
|
return m_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool empty() const
|
|
|
|
|
{
|
|
|
|
|
return 0 == m_size;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-26 20:42:35 +00:00
|
|
|
|
void clear()
|
|
|
|
|
{
|
|
|
|
|
if (!__has_trivial_destructor(Cell))
|
|
|
|
|
for (iterator it = begin(); it != end(); ++it)
|
|
|
|
|
it.ptr->~Cell();
|
|
|
|
|
|
|
|
|
|
memset(buf, 0, grower.bufSize() * sizeof(*buf));
|
|
|
|
|
m_size = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-17 02:01:03 +00:00
|
|
|
|
size_t getBufferSizeInBytes() const
|
|
|
|
|
{
|
2014-05-22 00:22:48 +00:00
|
|
|
|
return grower.bufSize() * sizeof(Cell);
|
2014-03-17 02:01:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef DBMS_HASH_MAP_COUNT_COLLISIONS
|
|
|
|
|
size_t getCollisions() const
|
|
|
|
|
{
|
|
|
|
|
return collisions;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
};
|