#include "assert.h" namespace stingray_plugin_foundation { template HashSet::HashSet(Allocator &a) : _data(), _used(0), _buckets(0), _spill_unused(0), _spill_freelist(END_OF_FREELIST), _allocator(a) {} template HashSet::HashSet(unsigned buckets, unsigned spill, Allocator &a) : _data(), _used(0), _buckets(buckets), _spill_unused(spill), _spill_freelist(END_OF_FREELIST) , _allocator(a) { allocate_data(buckets + spill); for (unsigned i=0; i<_data.size; ++i) _data.marker[i] = UNUSED; } template HashSet::HashSet(const HashSet &o) : _hash(o._hash), _equal(o._equal), _data(), _used(o._used), _buckets(o._buckets), _spill_unused(o._spill_unused), _spill_freelist(o._spill_freelist), _allocator(o._allocator) { allocate_data(o._data.size); for (unsigned i = 0; i < _data.size; ++i) { if (o._data.marker[i] != UNUSED && (o._data.marker[i] & 0x80000000) == 0) { construct(_data.key[i], IS_ALLOCATOR_AWARE_TYPE(K)()); _data.key[i] = o._data.key[i]; } _data.marker[i] = o._data.marker[i]; } } template void HashSet::operator=(const HashSet &o) { if (this == &o) return; clear(); _hash = o._hash; _equal = o._equal; _allocator.deallocate(_data.marker); _data.marker = nullptr; allocate_data(o._data.size); for (unsigned i = 0; i < _data.size; ++i) { if (o._data.marker[i] != UNUSED && (o._data.marker[i] & 0x80000000) == 0) { construct(_data.key[i], IS_ALLOCATOR_AWARE_TYPE(K)()); _data.key[i] = o._data.key[i]; } _data.marker[i] = o._data.marker[i]; } _used = o._used; _buckets = o._buckets; _spill_unused = o._spill_unused; _spill_freelist = o._spill_freelist; } template HashSet::~HashSet() { clear(); _allocator.deallocate(_data.marker); } template void HashSet::reserve(unsigned items) { unsigned buckets = int(items * 1.37f); if (buckets < int(19*1.37f)) buckets = int(19*1.37f); rehash(buckets); } template void HashSet::swap(HashSet &o) { XENSURE(&_allocator == &o._allocator); std::swap(_hash, o._hash); std::swap(_equal, o._equal); std::swap(_data.size, o._data.size); std::swap(_data.marker, o._data.marker); std::swap(_data.key, o._data.key); std::swap(_used, o._used); std::swap(_buckets, o._buckets); std::swap(_spill_unused, o._spill_unused); std::swap(_spill_freelist, o._spill_freelist); } template Allocator & HashSet::allocator() const { return _allocator; } template unsigned HashSet::size() const { return _used; } template bool HashSet::empty() const { return _used == 0; } template template unsigned HashSet::find_or_fail(const KEY_EQ &k) const { if (empty()) return END_OF_LIST; unsigned i = hash(k); if (_data.marker[i] == UNUSED) return END_OF_LIST; while (true) { if (i == END_OF_LIST || _equal(k, _data.key[i])) return i; i = _data.marker[i]; } } template template unsigned HashSet::find_or_make(const KEY_EQ &k) { unsigned i = hash(k); if (_data.marker[i] == UNUSED) { ++_used; construct(_data.key[i], IS_ALLOCATOR_AWARE_TYPE(K)()); _data.key[i] = k; _data.marker[i] = END_OF_LIST; return i; } while (true) { if (_equal(k, _data.key[i]) ) return i; if (_data.marker[i] == END_OF_LIST) { unsigned j = allocate_spill(); _data.marker[i] = j; i = j; construct(_data.key[i], IS_ALLOCATOR_AWARE_TYPE(K)()); _data.key[i] = k; _data.marker[i] = END_OF_LIST; return j; } else i = _data.marker[i]; } } template template void HashSet::find_and_erase(const KEY_EQ &k) { unsigned i = hash(k); if (_data.marker[i] == UNUSED) return; if (_equal(k, _data.key[i])) { if (_data.marker[i] == END_OF_LIST) { _data.marker[i] = UNUSED; destruct(_data.key[i]); --_used; } else { unsigned del = _data.marker[i]; _data.marker[i] = _data.marker[del]; _data.key[i] = _data.key[del]; destruct(_data.key[del]); free_spill(del); } return; } unsigned prev = i; while (true) { if (_equal(k, _data.key[i]) ) { _data.marker[prev] = _data.marker[i]; destruct(_data.key[i]); free_spill(i); return; } if (_data.marker[i] == END_OF_LIST) return; prev = i; i = _data.marker[i]; } } template template bool HashSet::has(const KEY_EQ &k) const { return find_or_fail(k) != END_OF_LIST; } template void HashSet::clear() { _used = 0; _spill_freelist = END_OF_FREELIST; _spill_unused = _data.size - _buckets; for (unsigned i = 0; i<_data.size; ++i) { if (bucket_valid(i)) destruct(_data.key[i]); _data.marker[i] = UNUSED; } } template template void HashSet::insert(const KEY_EQ &k) { if (full()) { unsigned i = find_or_fail(k); if (i != END_OF_LIST) return; grow(); } find_or_make(k); } template template void HashSet::erase(const KEY_EQ &k) { find_and_erase(k); } template void HashSet::rehash(unsigned new_buckets) { XENSURE(new_buckets >= _used); clear_freelist(); unsigned spill = int(new_buckets * 0.37f + 1); unsigned old_size = _data.size; if (_data.size == 0) { allocate_data(new_buckets + spill); _buckets = new_buckets; _spill_unused = spill; for (unsigned i = 0; i<_data.size; ++i) _data.marker[i] = UNUSED; return; } this_type new_set(new_buckets, spill, _allocator); for (unsigned o = 0; o typename HashSet::const_iterator HashSet::begin() const { return const_iterator(*this, 0); } template typename HashSet::const_iterator HashSet::end() const { return const_iterator(*this, num_buckets()); } template unsigned HashSet::num_buckets() const { return _data.size; } template bool HashSet::bucket_valid(unsigned i) const { return (_data.marker[i] & 0x80000000) == 0; } template const typename HashSet::key_type &HashSet::bucket_value(unsigned i) const { return _data.key[i]; } template typename HashSet::key_type &HashSet::bucket_value(unsigned i) { return _data.key[i]; } template bool HashSet::full() const { return _spill_unused == 0 && _spill_freelist == END_OF_FREELIST; } template void HashSet::grow() { do { unsigned new_buckets = _buckets * 2 + 1; if (new_buckets < 19) new_buckets = 19; rehash(new_buckets); } while (full()); } template template unsigned HashSet::hash(const KEY_EQ &k) const { return _hash(k) % _buckets; } template unsigned HashSet::allocate_spill() { ++_used; unsigned i = 0; if (_spill_freelist != END_OF_FREELIST) { i = _spill_freelist & 0x7fffffff; _spill_freelist = _data.marker[i]; return i; } else { XENSURE(_spill_unused > 0); i = _data.size - _spill_unused; --_spill_unused; } _data.marker[i] = UNUSED; return i; } template void HashSet::free_spill(unsigned i) { --_used; _data.marker[i] = _spill_freelist; _spill_freelist = i | 0x80000000; } template void HashSet::clear_freelist() { while (_spill_freelist != END_OF_FREELIST) { unsigned i = _spill_freelist & 0x7fffffff; _spill_freelist = _data.marker[i]; _data.marker[i] = UNUSED; } } template void HashSet::allocate_data(unsigned count) { XASSERT(_data.marker == nullptr, "Data must be deallocated/cleared before allocating new data"); if (count == 0) { _data = Data(); return; } auto marker_size = (((sizeof(unsigned) * count) + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*); auto key_size = sizeof(key_type) * count; auto data_size = marker_size + key_size; _data.marker = (unsigned*)_allocator.allocate(data_size); _data.key = (key_type*)&(((char*)_data.marker)[marker_size]); _data.size = count; } template template void HashSet::serialize(STREAM &stream) { unsigned n = _used; stream & n; if (stream.is_output()) { iterator it(*this, 0); iterator end(*this, num_buckets()); for (; it != end; ++it) stream & *it; } else { for (unsigned i=0; i