BitVector.hpp #7

  • //
  • guest/
  • ShadauxCat/
  • Sprawl/
  • Mainline/
  • collections/
  • BitVector.hpp
  • View
  • Commits
  • Open Download .zip Download (6 KB)
#pragma once

#include "../common/compat.hpp"
#include "../string/StringBuilder.hpp"
#include "../string/String.hpp"
#include <limits>
#include <stdlib.h>

namespace sprawl
{
	namespace collections
	{
		template<uint64_t maxBits>
		class BitSet;
		class BitVector;
	}
}

template<uint64_t maxBits>
class sprawl::collections::BitSet
{
public:
	BitSet()
		: m_bitArray()
	{
		Reset();
	}

	void Set(uint64_t bit)
	{
		size_t offset = getOffset_(bit);
		m_bitArray[offset] |= (uint64_t(1) << bit);
	}

	void Unset(uint64_t bit)
	{
		size_t offset = getOffset_(bit);
		m_bitArray[offset] &= ~(uint64_t(1) << bit);
	}

	void Flip(uint64_t bit)
	{
		size_t offset = getOffset_(bit);
		m_bitArray[offset] ^= (uint64_t(1) << bit);
	}

	bool HasBit(uint64_t bit) const
	{
		size_t offset = getOffset_(bit);
		return ((m_bitArray[offset] & (uint64_t(1) << bit)) != 0);
	}

	constexpr uint64_t Size() const
	{
		return maxBits;
	}

	bool Any() const
	{
		uint64_t testArray[sizeof(m_bitArray) / sizeof(uint64_t)];
		memset(testArray, 0, sizeof(testArray));
		return (memcmp(testArray, m_bitArray, sizeof(m_bitArray)) != 0);
	}

	bool None() const
	{
		return !Any();
	}

	bool All() const
	{
		//Memcmp would be nice here, but since we might have extra unused bits,
		//getting the right value to compare against is a bit tough.
		for(size_t i = 0; i < Size(); ++i)
		{
			if(!HasBit(i))
			{
				return false;
			}
		}
		return true;
	}

	void Reset()
	{
		memset(m_bitArray, 0, sizeof(m_bitArray));
	}

	uint64_t Count() const
	{
		uint64_t result = 0;
		size_t const numIndexes = sizeof(m_bitArray)/sizeof(int64_t);
		for(size_t i = 0; i < numIndexes; ++i)
		{
			result += countBitsSet_(m_bitArray[i]);
		}
		return result;
	}

	sprawl::String ToString()
	{
		sprawl::StringBuilder builder(maxBits, false);
		for(size_t i = maxBits; i > 0; --i)
		{
			if(HasBit(i-1))
			{
				builder << "1";
			}
			else
			{
				builder << "0";
			}
		}
		return builder.Str();
	}

private:
	uint64_t countBitsSet_(uint64_t value) const
	{
		uint64_t result = 0;
		for(uint64_t i = 0; i < 64; ++i)
		{
			if((value & (uint64_t(1) << i)) != 0)
			{
				++result;
			}
		}
		return result;
	}

	size_t getOffset_(uint64_t& bit) const
	{
		size_t offset = bit / 64;
		bit = bit % 64;
		return offset;
	}

	uint64_t m_bitArray[(maxBits / 64) + 1];
};


class sprawl::collections::BitVector
{
public:
	BitVector()
		: m_bitArray(new uint64_t[1])
		, m_size(0)
	{
		Reset();
	}

	~BitVector()
	{
		delete[] m_bitArray;
	}

	BitVector(BitVector const& other)
		: m_bitArray(new uint64_t[other.m_size / 64 + 1])
		, m_size(other.m_size)
	{
		memcpy(m_bitArray, other.m_bitArray, (other.m_size / 64 + 1) * sizeof(uint64_t));
	}

	BitVector(BitVector&& other)
		: m_bitArray(other.m_bitArray)
		, m_size(other.m_size)
	{
		other.m_bitArray = new uint64_t[1];
		other.m_size = 0;
	}

	BitVector& operator=(BitVector const& other)
	{
		m_bitArray = new uint64_t[other.m_size / 64 + 1];
		m_size = other.m_size;
		memcpy(m_bitArray, other.m_bitArray, (other.m_size / 64 + 1) * sizeof(uint64_t));
		return *this;
	}

	BitVector& operator=(BitVector&& other)
	{
		m_bitArray = other.m_bitArray;
		m_size = other.m_size;
		other.m_bitArray = new uint64_t[1];
		other.m_size = 0;
		return *this;
	}

	void Set(uint64_t bit)
	{
		size_t offset = getOffset_(bit);
		checkGrow_(offset, bit);
		m_bitArray[offset] |= (uint64_t(1) << bit);
	}

	void Unset(uint64_t bit)
	{
		size_t offset = getOffset_(bit);
		checkGrow_(offset, bit);
		m_bitArray[offset] &= ~(uint64_t(1) << bit);
	}

	void Flip(uint64_t bit)
	{
		size_t offset = getOffset_(bit);
		checkGrow_(offset, bit);
		m_bitArray[offset] ^= (uint64_t(1) << bit);
	}

	bool HasBit(uint64_t bit) const
	{
		size_t offset = getOffset_(bit);
		if(offset > m_size / 64)
		{
			return false;
		}
		return ((m_bitArray[offset] & (uint64_t(1) << bit)) != 0);
	}

	uint64_t Size() const
	{
		return m_size;
	}

	bool Any() const
	{
		uint64_t* testArray = (uint64_t*)alloca((m_size / 64 + 1) * sizeof(uint64_t));
		memset(testArray, 0, (m_size / 64 + 1) * sizeof(uint64_t));
		return (memcmp(testArray, m_bitArray, ((m_size / 64) + 1) * sizeof(uint64_t)) != 0);
	}

	bool None() const
	{
		return !Any();
	}

	bool All() const
	{
		//Memcmp would be nice here, but since we might have extra unused bits,
		//getting the right value to compare against is a bit tough.
		for(size_t i = 0; i < Size(); ++i)
		{
			if(!HasBit(i))
			{
				return false;
			}
		}
		return true;
	}

	void Reset()
	{
		memset(m_bitArray, 0, ((m_size / 64) + 1) * sizeof(uint64_t));
	}

	uint64_t Count() const
	{
		uint64_t result = 0;
		size_t const numIndexes = m_size / 64 + 1;
		for(size_t i = 0; i < numIndexes; ++i)
		{
			result += countBitsSet_(m_bitArray[i]);
		}
		return result;
	}

	sprawl::String ToString()
	{
		sprawl::StringBuilder builder(m_size, false);
		for(size_t i = m_size; i > 0; --i)
		{
			if(HasBit(i-1))
			{
				builder << "1";
			}
			else
			{
				builder << "0";
			}
		}
		return builder.Str();
	}

private:
	void checkGrow_(uint64_t newOffset, uint64_t bit)
	{
		if(newOffset > (m_size / 64))
		{
			uint64_t* newArray = new uint64_t[newOffset + 1];
			memset(newArray, 0, (newOffset + 1) * sizeof(uint64_t));
			memcpy(newArray, m_bitArray, ((m_size / 64) + 1) * sizeof(uint64_t));
			delete[] m_bitArray;
			m_bitArray = newArray;
		}

		uint64_t const fullBit = newOffset * 64 + bit;

		if(m_size <= fullBit)
		{
			m_size = fullBit + 1;
		}
	}

	uint64_t countBitsSet_(uint64_t value) const
	{
		uint64_t result = 0;
		for(uint64_t i = 0; i < 64; ++i)
		{
			if((value & (uint64_t(1) << i)) != 0)
			{
				++result;
			}
		}
		return result;
	}

	size_t getOffset_(uint64_t& bit) const
	{
		size_t offset = bit / 64;
		bit = bit % 64;
		return offset;
	}

	uint64_t* m_bitArray;
	size_t m_size;
};
# Change User Description Committed
#7 19906 ShadauxCat - Added tag, compile time string type
- Since tag requires visual studio 2015, removed compatibility code for earlier versions of visual studio
- Improved compiler detection
- Added endianness detection
- Added template if/else helper
- Fixed bug with murmur3 64 bit
- Added seed argument for murmur3

#review-19907
#6 14216 ShadauxCat -Moved some global sprawl::Strings into local scope in json serialization test because of initialization order issues in the memory allocator on mac.
This is a temporary fix, and a real fix will come by making the pool allocator work in explicitly-sized pieces and putting all the code for those pieces into a cpp file.
-Fixed a large number of warnings on mac/linux that were exposed by fixes to csbuild
-Fixed compile errors on mac due to malloc and alloca not being defined, fixed by #include <stdlib.h> in appropriate places
-Fixed mac os x trying to link against pthread erroneously
-Provided os x implementation of time library
-Fixed compile errors on os x due to std::unordered_map whining about the difference between an allocator that allocates std::pair<key, value> and one that allocates std::pair<key const, value>, which, of course, is that the allocator will be no different at all.
-Fixed an actual issue where one unordered_map was allocating only key_type instead of std::pair<key_type, value_type>
-Fixed a memory leak where coroutine objects would never be cleaned up because either Yield() or reactivate_() will never return (and thus never clean up their stack memory and thus never release any dynamic memory held in stack objects) depending on the situation - if the function runs to completion, reactivate_() never returns after calling swapcontext(); meanwhile, if the function does not run to completion, Yield() never returns after calling Pause(). This behavior will need to be well-documented because it will affect client-side code as well. Stack memory within a coroutine should not rely on RAII behavior.
-Fixed compile failure when creating a StlWrapper with a const value_type

#review-14217
#5 14121 ShadauxCat -Fixed msvc compile errors (msvc sucks at decltype, btw...)
-Fixed BitVector and BitSet being broken on msvc due to 1L being 32-bit rather than 64-bit
-Fixed Deque being broken on 32-bit systems due to an errant int64_t
-Changed types of deque indexes from size_t to ssize_t; since they have to be signed for a moment to handle circling the buffer, the sign bit is lost for capacity anyway, and using signed indexes means...
-Deque and Vector now support negative indexing a la python list

#review-14122
#4 14115 ShadauxCat Add missing copy/move/assignment/destructor for BitVector, variadic constructor for Coroutine

#review-14116
#3 14098 ShadauxCat Added BitVector (BitSet that dynamically increases in size as needed to accommodate the highest bit that gets set).
Later I'll add proper operators for both (i.e., BitSet(010101) & BitSet(011011) = BitSet(010001))

#review-14099
#2 14096 ShadauxCat Getting offset a more sensible way in bitset.

#review-14097
#1 14092 ShadauxCat Added BitSet (statically sized at compile time) and corresponding unit test.
BitVector (dynamically sized to the largest input it receives) to come next.

#review-14093