blob: 40fd6fc4000ce770e347d2aafd7a7936c611d5e9 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef DATASTAX_INTERNAL_HASH_INDEX_HPP
#define DATASTAX_INTERNAL_HASH_INDEX_HPP
#include "allocated.hpp"
#include "hash.hpp"
#include "macros.hpp"
#include "small_vector.hpp"
#include "string_ref.hpp"
#include "utils.hpp"
#include <stdint.h>
// This can be decreased to reduce hash collisions, but it will require
// additional memory.
#define CASS_LOAD_FACTOR 0.75
namespace datastax { namespace internal { namespace core {
typedef SmallVector<size_t, 4> IndexVec;
template <class T>
struct HashTableEntry {
HashTableEntry()
: index(0)
, next(NULL) {}
// Requires a "name" String or datastax::StringRef field
size_t index;
T* next;
};
template <class T>
class CaseInsensitiveHashTable : public Allocated {
public:
typedef SmallVector<T, 16> EntryVec;
CaseInsensitiveHashTable(size_t capacity = 16);
CaseInsensitiveHashTable(const EntryVec& entries);
T& operator[](size_t index) { return entries_[index]; }
const T& operator[](size_t index) const { return entries_[index]; }
size_t get_indices(StringRef name, IndexVec* result) const;
size_t add(const T& entry);
const EntryVec& entries() const { return entries_; }
void set_entries(const EntryVec& entries);
size_t size() const { return entries_.size(); }
private:
void add_index(T* entry);
void reset(size_t capacity);
void resize(size_t new_capacity);
void reindex();
private:
size_t index_mask_;
SmallVector<T*, 32> index_;
EntryVec entries_;
private:
DISALLOW_COPY_AND_ASSIGN(CaseInsensitiveHashTable);
};
template <class T>
CaseInsensitiveHashTable<T>::CaseInsensitiveHashTable(size_t capacity) {
reset(capacity);
}
template <class T>
CaseInsensitiveHashTable<T>::CaseInsensitiveHashTable(const EntryVec& entries) {
set_entries(entries);
}
template <class T>
size_t CaseInsensitiveHashTable<T>::get_indices(StringRef name, IndexVec* result) const {
result->clear();
bool is_case_sensitive = false;
if (!name.data()) {
return 0;
}
if (name.size() > 0 && name.front() == '"' && name.back() == '"') {
is_case_sensitive = true;
name = name.substr(1, name.size() - 2);
}
size_t h = hash::fnv1a(name.data(), name.size(), ::tolower) & index_mask_;
size_t start = h;
while (index_[h] != NULL && !iequals(name, index_[h]->name)) {
h = (h + 1) & index_mask_;
if (h == start) {
return 0;
}
}
const T* entry = index_[h];
if (entry == NULL) {
return 0;
}
if (!is_case_sensitive) {
while (entry != NULL) {
result->push_back(entry->index);
entry = entry->next;
}
} else {
while (entry != NULL) {
if (name.equals(entry->name)) {
result->push_back(entry->index);
}
entry = entry->next;
}
}
return result->size();
}
template <class T>
size_t CaseInsensitiveHashTable<T>::add(const T& entry) {
size_t index = entries_.size();
size_t capacity = entries_.capacity();
if (index >= capacity) {
resize(2 * capacity);
}
entries_.push_back(entry);
T* back = &entries_.back();
back->index = index;
add_index(back);
return index;
}
template <class T>
void CaseInsensitiveHashTable<T>::set_entries(const EntryVec& entries) {
entries_.clear();
reset(entries.size());
for (size_t i = 0; i < entries.size(); ++i) {
add(entries[i]);
}
}
template <class T>
void CaseInsensitiveHashTable<T>::add_index(T* entry) {
size_t h = hash::fnv1a(entry->name.data(), entry->name.size(), ::tolower) & index_mask_;
if (index_[h] == NULL) {
index_[h] = entry;
} else {
// Use linear probing to find an open bucket
size_t start = h;
while (index_[h] != NULL && !iequals(entry->name, index_[h]->name)) {
h = (h + 1) & index_mask_;
if (h == start) {
return;
}
}
if (index_[h] == NULL) {
index_[h] = entry;
} else {
T* curr = index_[h];
while (curr->next != NULL) {
curr = curr->next;
}
curr->next = entry;
}
}
}
template <class T>
void CaseInsensitiveHashTable<T>::reset(size_t capacity) {
if (capacity < entries_.capacity()) {
capacity = entries_.capacity();
}
size_t index_capacity = next_pow_2(static_cast<size_t>(capacity / CASS_LOAD_FACTOR) + 1);
std::fill(index_.begin(), index_.end(), static_cast<T*>(NULL)); // Clear the old entries
index_.resize(index_capacity);
entries_.reserve(capacity);
index_mask_ = index_capacity - 1;
}
template <class T>
void CaseInsensitiveHashTable<T>::resize(size_t new_capacity) {
reset(new_capacity);
reindex();
}
template <class T>
void CaseInsensitiveHashTable<T>::reindex() {
for (size_t i = 0; i < entries_.size(); ++i) {
T* entry = &entries_[i];
entry->index = i;
add_index(entry);
}
}
}}} // namespace datastax::internal::core
#endif