#pragma once //hashset // //search: O(1) average; O(n) worst //insert: O(1) average; O(n) worst //remove: O(1) average; O(n) worst // //requirements: // auto T::hash() const -> u32; // auto T::operator==(const T&) const -> bool; namespace nall { template struct hashset { hashset() = default; hashset(u32 length) : length(bit::round(length)) {} hashset(const hashset& source) { operator=(source); } hashset(hashset&& source) { operator=(std::move(source)); } ~hashset() { reset(); } auto operator=(const hashset& source) -> hashset& { if(this == &source) return *this; reset(); if(source.pool) { for(u32 n : range(source.length)) { if(source.pool[n]) insert(*source.pool[n]); } } return *this; } auto operator=(hashset&& source) -> hashset& { if(this == &source) return *this; reset(); pool = source.pool; length = source.length; count = source.count; source.pool = nullptr; source.length = 8; source.count = 0; return *this; } explicit operator bool() const { return count; } auto capacity() const -> u32 { return length; } auto size() const -> u32 { return count; } auto reset() -> void { if(pool) { for(u32 n : range(length)) { if(pool[n]) { delete pool[n]; pool[n] = nullptr; } } delete[] pool; pool = nullptr; } length = 8; count = 0; } auto reserve(u32 size) -> void { if(length >= size) return; //ensure all items will fit into pool (with <= 50% load) and amortize growth size = bit::round(max(size, count << 1)); T** copy = new T*[size](); if(pool) { for(u32 n : range(length)) { if(pool[n]) { u32 hash = (*pool[n]).hash() & (size - 1); while(copy[hash]) if(++hash >= size) hash = 0; copy[hash] = pool[n]; pool[n] = nullptr; } } } delete[] pool; pool = copy; length = size; } auto find(const T& value) -> maybe { if(!pool) return nothing; u32 hash = value.hash() & (length - 1); while(pool[hash]) { if(value == *pool[hash]) return *pool[hash]; if(++hash >= length) hash = 0; } return nothing; } auto insert(const T& value) -> maybe { if(!pool) pool = new T*[length](); //double pool size when load is >= 50% if(count >= (length >> 1)) reserve(length << 1); u32 hash = value.hash() & (length - 1); while(pool[hash]) { if(value == *pool[hash]) return nothing; if(++hash >= length) hash = 0; } count++; pool[hash] = new T(value); return *pool[hash]; } #if 0 //does not work! todo: implement tombstones or rehashing to fill gaps. auto remove(const T& value) -> bool { if(!pool) return false; u32 hash = value.hash() & (length - 1); while(pool[hash]) { if(value == *pool[hash]) { delete pool[hash]; pool[hash] = nullptr; count--; return true; } if(++hash >= length) hash = 0; } return false; } #endif protected: T** pool = nullptr; u32 length = 8; //length of pool u32 count = 0; //number of objects inside of the pool }; }