# SOFTWARE.
#
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_subdirectory(spirv)
add_subdirectory(demo)
add_subdirectory(json)
#include <array>
#include <type_traits>
#include <string>
-#include "../spirv/spirv.h"
-#include "../util/optional.h"
+#include "spirv/spirv.h"
+#include "util/optional.h"
namespace vulkan_cpu
{
#ifndef GENERATE_SPIRV_PARSER_AST_H_
#define GENERATE_SPIRV_PARSER_AST_H_
-#include "../json/json.h"
+#include "json/json.h"
#include <cstdint>
#include <vector>
#include <string>
state.open_output_file();
write_file_comments(state, top_level.copyright);
write_file_guard_start(state);
- state << "#include <cstdint>\n";
+ state << "#include <cstdint>\n"
+ "#include \"util/bitset.h\"\n";
state << "\n";
write_namespaces_start(state, spirv_namespace_names);
state << "typedef std::uint32_t Word;\n";
extensions_list.push_back(extension);
std::sort(extensions_list.begin(), extensions_list.end());
state << "\n"
- "enum class Extension\n"
+ "enum class Extension : std::size_t\n"
"{\n";
{
auto push_indent = state.pushed_indent();
state << indent << extension << ",\n";
}
state << "};\n"
+ "\n"
+ "constexpr std::size_t Extension_count = "
+ << unsigned_dec_integer_literal(extensions_list.size())
+ << ";\n"
"\n"
"constexpr const char *get_extension_name(Extension extension) noexcept\n"
"{\n";
});
for(auto *operand_kind : operand_kinds)
{
- switch(operand_kind->category)
- {
- case ast::Operand_kinds::Operand_kind::Category::value_enum:
- case ast::Operand_kinds::Operand_kind::Category::bit_enum:
+ if(util::holds_alternative<ast::Operand_kinds::Operand_kind::Enumerants>(
+ operand_kind->value))
{
auto &enumerants =
util::get<ast::Operand_kinds::Operand_kind::Enumerants>(operand_kind->value);
}
push_indent.finish();
state << "};\n";
- break;
- }
-#warning finish
}
}
}
}
}
-}
\ No newline at end of file
+}
*
*/
#include <iostream>
-#include "../json/json.h"
-#include "../json/parser.h"
+#include "json/json.h"
+#include "json/parser.h"
#include "parser.h"
-#include "../util/optional.h"
+#include "util/optional.h"
#include "generate.h"
namespace vulkan_cpu
*
*/
#include "parser.h"
-#include "../util/optional.h"
+#include "util/optional.h"
#include <sstream>
#include <limits>
#include <iostream>
#include <cassert>
#include <string>
#include <vector>
-#include "../util/variant.h"
-#include "../json/json.h"
-#include "../json/parser.h"
+#include "util/variant.h"
+#include "json/json.h"
+#include "json/parser.h"
namespace vulkan_cpu
{
#include <cassert>
#include <tuple>
#include <algorithm>
-#include "../util/soft_float.h"
+#include "util/soft_float.h"
namespace vulkan_cpu
{
#include <cassert>
#include <cmath>
#include <list>
-#include "../util/variant.h"
-#include "../util/optional.h"
+#include "util/variant.h"
+#include "util/optional.h"
#include "location.h"
namespace vulkan_cpu
#include <cassert>
#include <algorithm>
#include <limits>
-#include "../util/soft_float.h"
+#include "util/soft_float.h"
namespace vulkan_cpu
{
#include "json.h"
#include "source.h"
#include "location.h"
-#include "../util/optional.h"
+#include "util/optional.h"
namespace vulkan_cpu
{
#
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
set(sources bit_intrinsics.cpp
+ bitset.cpp
copy_cv_ref.cpp
in_place.cpp
invoke.cpp
#if defined(__clang__)
#if defined(__apple_build_version__)
#if __clang_major__ > 5 || (__clang_major__ == 5 && __clang_minor__ >= 1)
-#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED 1
+#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED 1
#endif
#else
#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 4)
-#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED 1
+#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED 1
#endif
#endif
#elif defined(__INTEL_COMPILER)
#warning figure out icc version numbers for constexpr __builtin_clz and __builtin_ctz
#elif defined(__GNUC__)
// gcc supports constexpr __builtin_clz and __builtin_ctz before it supports c++14
-#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED 1
+#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED 1
#endif
#if 1
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
-#undef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
+#undef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
#endif
#endif
{
constexpr std::uint32_t clz4(std::uint8_t v) noexcept
{
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
return v == 0 ? 4 : __builtin_clz(v) - __builtin_clz(0x8U);
#else
typedef const std::uint_fast8_t LookupTableType[0x10];
constexpr std::uint32_t clz8(std::uint8_t v) noexcept
{
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
return v == 0 ? 8 : __builtin_clz(v) - __builtin_clz(0x80U);
#else
return ((v & 0xF0) == 0) ? 4 + clz4(v) : clz4(v >> 4);
constexpr std::uint32_t clz16(std::uint16_t v) noexcept
{
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
return v == 0 ? 16 : __builtin_clz(v) - (std::numeric_limits<int>::digits - 16);
#else
return ((v & 0xFF00U) == 0) ? 8 + clz8(v) : clz8(v >> 8);
constexpr std::uint32_t clz32(std::uint32_t v) noexcept
{
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
return v == 0 ? 32 : __builtin_clzl(v) - (std::numeric_limits<long>::digits - 32);
#else
return ((v & 0xFFFF0000UL) == 0) ? 16 + clz16(v) : clz16(v >> 16);
constexpr std::uint32_t clz64(std::uint64_t v) noexcept
{
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
return v == 0 ? 64 : __builtin_clzll(v) - (std::numeric_limits<long long>::digits - 64);
#else
return ((v & 0xFFFFFFFF00000000ULL) == 0) ? 32 + clz32(v) : clz32(v >> 32);
constexpr std::uint32_t ctz4(std::uint8_t v) noexcept
{
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
return v == 0 ? 4 : __builtin_ctz(v);
#else
typedef const std::uint_fast8_t LookupTableType[0x10];
constexpr std::uint32_t ctz8(std::uint8_t v) noexcept
{
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
return v == 0 ? 8 : __builtin_ctz(v);
#else
return ((v & 0xF0) == 0) ? ctz4(v) : 4 + ctz4(v >> 4);
constexpr std::uint32_t ctz16(std::uint16_t v) noexcept
{
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
return v == 0 ? 16 : __builtin_ctz(v);
#else
return ((v & 0xFF00U) == 0) ? ctz8(v) : 8 + ctz8(v >> 8);
constexpr std::uint32_t ctz32(std::uint32_t v) noexcept
{
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
return v == 0 ? 32 : __builtin_ctzl(v);
#else
return ((v & 0xFFFF0000UL) == 0) ? ctz16(v) : 16 + ctz16(v >> 16);
constexpr std::uint32_t ctz64(std::uint64_t v) noexcept
{
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
return v == 0 ? 64 : __builtin_ctzll(v);
#else
return ((v & 0xFFFFFFFF00000000ULL) == 0) ? ctz32(v) : 32 + ctz32(v >> 32);
#endif
}
+
+constexpr std::uint32_t popcount8(std::uint8_t v) noexcept
+{
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
+ return __builtin_popcount(v);
+#else
+ constexpr std::uint8_t lookup_table[0x10] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ };
+ return lookup_table[v & 0xF] + lookup_table[v >> 4];
+#endif
+}
+
+constexpr std::uint32_t popcount32(std::uint32_t v) noexcept
+{
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
+ return __builtin_popcountl(v);
+#else
+ constexpr std::uint32_t m1 = 0x5555'5555UL;
+ constexpr std::uint32_t m2 = 0x3333'3333UL;
+ constexpr std::uint32_t m4 = 0x0F0F'0F0FUL;
+ v -= (v >> 1) & m1;
+ v = (v & m2) + ((v >> 2) & m2);
+ v = (v & m4) + ((v >> 4) & m4);
+ return static_cast<std::uint32_t>(v * 0x0101'0101UL) >> 24;
+#endif
+}
+
+constexpr std::uint32_t popcount16(std::uint16_t v) noexcept
+{
+ return popcount32(v);
+}
+
+constexpr std::uint32_t popcount64(std::uint64_t v) noexcept
+{
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
+ return __builtin_popcountll(v);
+#else
+ constexpr std::uint64_t m1 = 0x5555'5555'5555'5555ULL;
+ constexpr std::uint64_t m2 = 0x3333'3333'3333'3333ULL;
+ constexpr std::uint64_t m4 = 0x0F0F'0F0F'0F0F'0F0FULL;
+ v -= (v >> 1) & m1;
+ v = (v & m2) + ((v >> 2) & m2);
+ v = (v & m4) + ((v >> 4) & m4);
+ return static_cast<std::uint64_t>(v * 0x0101'0101'0101'0101ULL) >> 56;
+#endif
+}
}
}
--- /dev/null
+/*
+ * Copyright 2017 Jacob Lifshay
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+// derived from
+// https://github.com/programmerjake/hashlife-voxels/blob/0dd91021a5b9caeffb7849b2114dca89204876bd/util/bitset.cpp
+
+#include "bitset.h"
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+#include <random>
+#include <vector>
+#include <string>
+#include <utility>
+
+namespace vulkan_cpu
+{
+namespace util
+{
+namespace detail
+{
+#if 0
+#warning testing bitset
+struct Bitset_nontemplate_base::Tester final
+{
+ template <std::size_t Bit_count>
+ static void check_unused_bits(const bitset<Bit_count> &value)
+ {
+ constexpr Word_type unused_bits =
+ Bit_count % word_bit_count ? ~((1ULL << (Bit_count % word_bit_count)) - 1ULL) : 0;
+ assert((value.get_word(value.word_count - 1) & unused_bits) == 0);
+ }
+ static void check_unused_bits(const bitset<0> &)
+ {
+ }
+ template <std::size_t Bit_count>
+ static void test_default_construct()
+ {
+ bitset<Bit_count> value;
+ for(std::size_t i = 0; i < value.word_count; i++)
+ assert(value.get_word(i) == 0);
+ check_unused_bits(value);
+ }
+ template <std::size_t Bit_count>
+ static void test_construct_from_ull()
+ {
+ for(std::size_t i = 0; i < std::numeric_limits<unsigned long long>::digits; i++)
+ {
+ bitset<Bit_count> value(1ULL << i);
+ check_unused_bits(value);
+ assert(bitset<Bit_count>(1ULL << i).to_ullong() == (1ULL << i) || i >= Bit_count);
+ }
+ }
+ template <std::size_t Bit_count>
+ static void test_reference_assign()
+ {
+ std::default_random_engine re;
+ std::uniform_int_distribution<unsigned long long> distribution;
+ for(std::size_t i = 0; i < 1000; i++)
+ {
+ bitset<Bit_count> src(distribution(re)), dest;
+ for(std::size_t j = 0; j < Bit_count; j++)
+ {
+ dest[j] = src[j];
+ check_unused_bits(src);
+ check_unused_bits(dest);
+ }
+ assert(src == dest);
+ }
+ }
+ template <std::size_t Bit_count>
+ static void test_reference_flip()
+ {
+ if(Bit_count == 0)
+ return;
+ std::default_random_engine re;
+ std::vector<bool> vector;
+ vector.resize(Bit_count, false);
+ bitset<Bit_count> value;
+ for(std::size_t i = 0; i < 1000; i++)
+ {
+ std::size_t index = std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+ vector[index].flip();
+ value[index].flip();
+ check_unused_bits(value);
+ for(std::size_t j = 0; j < Bit_count; j++)
+ assert(value[index] == static_cast<bool>(vector[index]));
+ }
+ }
+ template <std::size_t Bit_count>
+ static void test_test()
+ {
+ std::default_random_engine re;
+ std::vector<bool> vector;
+ vector.resize(Bit_count, false);
+ bitset<Bit_count> value;
+ if(Bit_count != 0)
+ {
+ for(std::size_t i = 0; i < 1000; i++)
+ {
+ std::size_t index =
+ std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+ vector[index].flip();
+ value[index].flip();
+ check_unused_bits(value);
+ }
+ }
+ for(std::size_t i = 0; i < Bit_count + 1000; i++)
+ {
+ bool threw = false;
+ bool result = false;
+ try
+ {
+ result = value.test(i);
+ }
+ catch(std::out_of_range &)
+ {
+ threw = true;
+ }
+ if(i >= Bit_count)
+ {
+ assert(threw);
+ }
+ else
+ {
+ assert(!threw);
+ assert(result == vector[i]);
+ }
+ }
+ }
+ template <std::size_t Bit_count>
+ static void test_all_none_any_and_count_helper(const std::vector<bool> &vector,
+ const bitset<Bit_count> &value)
+ {
+ std::size_t set_bit_count = 0;
+ for(std::size_t i = 0; i < Bit_count; i++)
+ if(vector[i])
+ set_bit_count++;
+ assert(value.all() == (set_bit_count == Bit_count));
+ assert(value.any() == (set_bit_count != 0));
+ assert(value.none() == (set_bit_count == 0));
+ assert(value.count() == set_bit_count);
+ }
+ template <std::size_t Bit_count>
+ static void test_all_none_any_and_count()
+ {
+ std::default_random_engine re;
+ std::vector<bool> vector;
+ vector.resize(Bit_count, false);
+ bitset<Bit_count> value;
+ test_all_none_any_and_count_helper(vector, value);
+ if(Bit_count != 0)
+ {
+ for(std::size_t i = 0; i < 1000; i++)
+ {
+ std::size_t index =
+ std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+ vector[index].flip();
+ value[index].flip();
+ check_unused_bits(value);
+ test_all_none_any_and_count_helper(vector, value);
+ }
+ }
+ for(std::size_t i = 0; i < Bit_count; i++)
+ {
+ value[i] = true;
+ vector[i] = true;
+ check_unused_bits(value);
+ test_all_none_any_and_count_helper(vector, value);
+ }
+ }
+ template <std::size_t Bit_count>
+ static void test_and_or_and_xor_helper(const std::vector<bool> &vector1,
+ const std::vector<bool> &vector2,
+ const bitset<Bit_count> &bitset1,
+ const bitset<Bit_count> &bitset2)
+ {
+ bitset<Bit_count> dest_bitset_and = bitset1 & bitset2;
+ bitset<Bit_count> dest_bitset_or = bitset1 | bitset2;
+ bitset<Bit_count> dest_bitset_xor = bitset1 ^ bitset2;
+ check_unused_bits(dest_bitset_and);
+ check_unused_bits(dest_bitset_or);
+ check_unused_bits(dest_bitset_xor);
+ for(std::size_t i = 0; i < Bit_count; i++)
+ {
+ assert(dest_bitset_and[i] == (vector1[i] && vector2[i]));
+ assert(dest_bitset_or[i] == (vector1[i] || vector2[i]));
+ assert(dest_bitset_xor[i] == (static_cast<bool>(vector1[i]) != vector2[i]));
+ }
+ }
+ template <std::size_t Bit_count>
+ static void test_and_or_and_xor()
+ {
+ std::default_random_engine re;
+ std::vector<bool> vector1, vector2;
+ vector1.resize(Bit_count, false);
+ vector2.resize(Bit_count, false);
+ bitset<Bit_count> bitset1, bitset2;
+ test_and_or_and_xor_helper(vector1, vector2, bitset1, bitset2);
+ if(Bit_count != 0)
+ {
+ for(std::size_t i = 0; i < 2000; i++)
+ {
+ std::size_t index =
+ std::uniform_int_distribution<std::size_t>(0, Bit_count * 2 - 1)(re);
+ bool is_second_bit_set = index >= Bit_count;
+ index %= Bit_count;
+ if(is_second_bit_set)
+ {
+ vector2[index].flip();
+ bitset2[index].flip();
+ }
+ else
+ {
+ vector1[index].flip();
+ bitset1[index].flip();
+ }
+ check_unused_bits(bitset1);
+ check_unused_bits(bitset2);
+ test_and_or_and_xor_helper(vector1, vector2, bitset1, bitset2);
+ }
+ }
+ for(std::size_t i = 0; i < Bit_count; i++)
+ {
+ bitset1[i] = true;
+ vector1[i] = true;
+ check_unused_bits(bitset1);
+ check_unused_bits(bitset2);
+ test_and_or_and_xor_helper(vector1, vector2, bitset1, bitset2);
+ }
+ for(std::size_t i = 0; i < Bit_count; i++)
+ {
+ bitset2[i] = true;
+ vector2[i] = true;
+ check_unused_bits(bitset1);
+ check_unused_bits(bitset2);
+ test_and_or_and_xor_helper(vector1, vector2, bitset1, bitset2);
+ }
+ }
+ template <std::size_t Bit_count>
+ static void test_not()
+ {
+ std::default_random_engine re;
+ std::vector<bool> vector;
+ vector.resize(Bit_count, false);
+ bitset<Bit_count> value;
+ if(Bit_count != 0)
+ {
+ for(std::size_t i = 0; i < 1000; i++)
+ {
+ std::size_t index =
+ std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+ vector[index].flip();
+ value[index].flip();
+ check_unused_bits(value);
+ bitset<Bit_count> bitset_not = ~value;
+ check_unused_bits(bitset_not);
+ for(std::size_t j = 0; j < Bit_count; j++)
+ assert(vector[j] == !bitset_not[j]);
+ }
+ }
+ }
+ template <std::size_t Bit_count>
+ static void test_shift_helper(const std::vector<bool> &vector, const bitset<Bit_count> &value)
+ {
+ for(std::size_t shift_count = 0; shift_count < Bit_count * 2 + 1; shift_count++)
+ {
+ bitset<Bit_count> bitset_shifted_left = value << shift_count;
+ bitset<Bit_count> bitset_shifted_right = value >> shift_count;
+ check_unused_bits(bitset_shifted_left);
+ check_unused_bits(bitset_shifted_right);
+ for(std::size_t i = 0; i < Bit_count; i++)
+ {
+ assert(bitset_shifted_left[i]
+ == (i < shift_count ? false : static_cast<bool>(vector[i - shift_count])));
+ assert(bitset_shifted_right[i] == (shift_count >= Bit_count - i ?
+ false :
+ static_cast<bool>(vector[i + shift_count])));
+ }
+ }
+ }
+ template <std::size_t Bit_count>
+ static void test_shift()
+ {
+ std::default_random_engine re;
+ std::vector<bool> vector;
+ vector.resize(Bit_count, false);
+ bitset<Bit_count> value;
+ test_shift_helper(vector, value);
+ if(Bit_count != 0)
+ {
+ for(std::size_t i = 0; i < 1000; i++)
+ {
+ std::size_t index =
+ std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+ vector[index].flip();
+ value[index].flip();
+ check_unused_bits(value);
+ test_shift_helper(vector, value);
+ }
+ }
+ for(std::size_t i = 0; i < Bit_count; i++)
+ {
+ value[i] = true;
+ vector[i] = true;
+ check_unused_bits(value);
+ test_shift_helper(vector, value);
+ }
+ }
+ template <std::size_t Bit_count>
+ static void test_global_set_and_reset()
+ {
+ bitset<Bit_count> value;
+ value.reset();
+ check_unused_bits(value);
+ assert(value.none());
+ value.set();
+ check_unused_bits(value);
+ assert(value.all());
+ }
+ template <std::size_t Bit_count>
+ static void test_find_helper(const std::string &string, const bitset<Bit_count> &value)
+ {
+ for(std::size_t i = 0; i < Bit_count; i++)
+ assert(string[i] == (value[i] ? '1' : '0'));
+ for(std::size_t start = 0; start <= Bit_count; start++)
+ {
+ assert(string.find_first_of('1', start) == value.find_first(true, start));
+ assert(string.find_first_not_of('1', start) == value.find_first(false, start));
+ assert(string.find_last_of('1', start) == value.find_last(true, start));
+ assert(string.find_last_not_of('1', start) == value.find_last(false, start));
+ }
+ }
+ template <std::size_t Bit_count>
+ static void test_find()
+ {
+ std::default_random_engine re;
+ std::string string;
+ string.resize(Bit_count, '0');
+ bitset<Bit_count> value;
+ test_find_helper(string, value);
+ if(Bit_count != 0)
+ {
+ for(std::size_t i = 0; i < 1000; i++)
+ {
+ std::size_t index =
+ std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+ string[index] = (string[index] == '0' ? '1' : '0');
+ value[index].flip();
+ check_unused_bits(value);
+ test_find_helper(string, value);
+ }
+ }
+ for(std::size_t i = 0; i < Bit_count; i++)
+ {
+ value[i] = true;
+ string[i] = '1';
+ check_unused_bits(value);
+ test_find_helper(string, value);
+ }
+ if(Bit_count != 0)
+ {
+ for(std::size_t i = 0; i < 1000; i++)
+ {
+ std::size_t index =
+ std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+ string[index] = (string[index] == '0' ? '1' : '0');
+ value[index].flip();
+ check_unused_bits(value);
+ test_find_helper(string, value);
+ }
+ }
+ }
+ template <std::size_t Bit_count>
+ static char test()
+ {
+ std::cout << "testing bitset<" << Bit_count << ">" << std::endl;
+ test_default_construct<Bit_count>();
+ test_construct_from_ull<Bit_count>();
+ test_reference_assign<Bit_count>();
+ test_reference_flip<Bit_count>();
+ test_test<Bit_count>();
+ test_all_none_any_and_count<Bit_count>();
+ test_and_or_and_xor<Bit_count>();
+ test_not<Bit_count>();
+ test_shift<Bit_count>();
+ test_global_set_and_reset<Bit_count>();
+ test_find<Bit_count>();
+ return 0;
+ }
+ template <typename... Args>
+ static void test_helper(Args...)
+ {
+ }
+ template <std::size_t... Bit_counts>
+ static void test(std::index_sequence<Bit_counts...>)
+ {
+ test_helper(test<Bit_counts>()...);
+ }
+ struct Test_runner final
+ {
+ Test_runner()
+ {
+ test(std::make_index_sequence<128>());
+ std::exit(0);
+ }
+ };
+ static Test_runner test_runner;
+};
+Bitset_nontemplate_base::Tester::Test_runner Bitset_nontemplate_base::Tester::test_runner;
+#endif
+}
+}
+}
--- /dev/null
+/*
+ * Copyright 2017 Jacob Lifshay
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef UTIL_BITSET_H_
+#define UTIL_BITSET_H_
+
+#include "bit_intrinsics.h"
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+#include <utility>
+#include <string>
+#include <iosfwd>
+#include <stdexcept>
+
+// I have my own bitset implementation because std::bitset is not completely constexpr.
+
+// derived from
+// https://github.com/programmerjake/hashlife-voxels/blob/0dd91021a5b9caeffb7849b2114dca89204876bd/util/bitset.h
+
+namespace vulkan_cpu
+{
+namespace util
+{
+template <std::size_t Bit_count>
+class bitset;
+namespace detail
+{
+class Bitset_nontemplate_base_helper
+{
+protected:
+ static constexpr bool is_power_of_2(std::size_t v) noexcept
+ {
+ return (v & (v - 1)) == 0;
+ }
+};
+
+struct Bitset_nontemplate_base : public Bitset_nontemplate_base_helper
+{
+protected:
+ struct Tester;
+
+public:
+ typedef std::uintptr_t Word_type;
+ static constexpr std::size_t word_bit_count = std::numeric_limits<Word_type>::digits;
+ static_assert(is_power_of_2(word_bit_count), "word_bit_count is not a power of 2");
+ static constexpr std::size_t constexpr_min(std::size_t a, std::size_t b) noexcept
+ {
+ return a > b ? b : a;
+ }
+ static constexpr std::size_t get_word_index(std::size_t bit_index) noexcept
+ {
+ return bit_index / word_bit_count;
+ }
+ static constexpr Word_type get_word_mask(std::size_t bit_index) noexcept
+ {
+ return static_cast<Word_type>(1) << (bit_index % word_bit_count);
+ }
+ static constexpr std::size_t get_word_count(std::size_t bit_count) noexcept
+ {
+ return (bit_count + (word_bit_count - 1)) / word_bit_count;
+ }
+};
+
+template <std::size_t Word_count>
+class Bitset_base : protected Bitset_nontemplate_base
+{
+protected:
+ static constexpr std::size_t word_count = Word_count;
+ Word_type words[Word_count]; // little endian order
+ constexpr Bitset_base() noexcept : words{}
+ {
+ }
+ constexpr Bitset_base(unsigned long long val) noexcept
+ : Bitset_base(
+ val,
+ std::make_index_sequence<constexpr_min(
+ get_word_count(std::numeric_limits<unsigned long long>::digits), Word_count)>())
+ {
+ }
+ constexpr Word_type get_word(std::size_t word_index) const noexcept
+ {
+ return words[word_index];
+ }
+ constexpr void set_word(std::size_t word_index, Word_type word_value) noexcept
+ {
+ words[word_index] = word_value;
+ }
+ constexpr bool equals(const Bitset_base &rt) const noexcept
+ {
+ for(std::size_t i = 0; i < word_count; i++)
+ if(words[i] != rt.words[i])
+ return false;
+ return true;
+ }
+
+private:
+ template <std::size_t... Indexes>
+ constexpr Bitset_base(unsigned long long val, std::index_sequence<Indexes...>) noexcept
+ : words{
+ static_cast<Word_type>(val >> Indexes * word_bit_count)...,
+ }
+ {
+ }
+};
+
+template <>
+class Bitset_base<0> : protected Bitset_nontemplate_base
+{
+protected:
+ static constexpr std::size_t word_count = 0;
+ constexpr Bitset_base() noexcept
+ {
+ }
+ constexpr Bitset_base(unsigned long long) noexcept
+ {
+ }
+ constexpr Word_type get_word(std::size_t word_index) const noexcept
+ {
+ return (static_cast<void>(word_index), 0);
+ }
+ constexpr void set_word(std::size_t word_index, Word_type word_value) noexcept
+ {
+ static_cast<void>(word_index);
+ static_cast<void>(word_value);
+ }
+ constexpr bool equals(const Bitset_base &rt) const noexcept
+ {
+ return true;
+ }
+
+public:
+ constexpr unsigned long long to_ullong() const
+ {
+ return 0;
+ }
+};
+}
+
+template <std::size_t Bit_count>
+class bitset final
+ : public detail::Bitset_base<detail::Bitset_nontemplate_base::get_word_count(Bit_count)>
+{
+private:
+ friend struct detail::Bitset_nontemplate_base::Tester;
+ static constexpr std::size_t bit_count = Bit_count;
+ typedef detail::Bitset_base<detail::Bitset_nontemplate_base::get_word_count(Bit_count)> Base;
+ using typename Base::Word_type;
+ using Base::word_count;
+ using Base::get_word;
+ using Base::set_word;
+ using Base::get_word_count;
+ using Base::get_word_mask;
+ using Base::get_word_index;
+ using Base::word_bit_count;
+
+private:
+ constexpr Word_type get_word_checked(std::size_t word_index) const noexcept
+ {
+ return word_index < word_count ? get_word(word_index) : 0;
+ }
+
+public:
+ constexpr bitset() noexcept : Base()
+ {
+ }
+ constexpr bitset(unsigned long long val) noexcept
+ : Base(bit_count >= std::numeric_limits<unsigned long long>::digits ?
+ val :
+ val & ((1ULL << bit_count) - 1ULL))
+ {
+ }
+ class reference final
+ {
+ template <std::size_t>
+ friend class bitset;
+
+ private:
+ bitset *base;
+ std::size_t bit_index;
+ constexpr reference(bitset *base, std::size_t bit_index) noexcept : base(base),
+ bit_index(bit_index)
+ {
+ }
+
+ public:
+ constexpr reference &operator=(
+ const reference &rt) noexcept // assigns referenced value, not this class
+ {
+ return operator=(static_cast<bool>(rt));
+ }
+ constexpr reference &operator=(bool value) noexcept
+ {
+ auto mask = get_word_mask(bit_index);
+ auto word = base->get_word(get_word_index(bit_index));
+ if(value)
+ word |= mask;
+ else
+ word &= ~mask;
+ base->set_word(get_word_index(bit_index), word);
+ return *this;
+ }
+ constexpr operator bool() const noexcept
+ {
+ return static_cast<const bitset *>(base)->operator[](bit_index);
+ }
+ constexpr bool operator~() const noexcept
+ {
+ return !operator bool();
+ }
+ constexpr reference &flip() noexcept
+ {
+ auto mask = get_word_mask(bit_index);
+ auto word = base->get_word(get_word_index(bit_index));
+ word ^= mask;
+ base->set_word(get_word_index(bit_index), word);
+ return *this;
+ }
+ };
+ constexpr bool operator==(const bitset &rt) const noexcept
+ {
+ return this->equals(rt);
+ }
+ constexpr bool operator!=(const bitset &rt) const noexcept
+ {
+ return !this->equals(rt);
+ }
+ constexpr reference operator[](std::size_t bit_index) noexcept
+ {
+ return reference(this, bit_index);
+ }
+ constexpr bool operator[](std::size_t bit_index) const noexcept
+ {
+ return get_word_mask(bit_index) & get_word(get_word_index(bit_index));
+ }
+ constexpr bool test(std::size_t bit_index) const
+ {
+ if(bit_index >= bit_count)
+ throw std::out_of_range("bit_index out of range in bitset::test");
+ return operator[](bit_index);
+ }
+ constexpr bool all() const noexcept
+ {
+ if(bit_count == 0)
+ return true;
+ for(std::size_t i = 0; i < get_word_index(bit_count - 1); i++)
+ if(get_word(i) != static_cast<Word_type>(-1))
+ return false;
+ return get_word(get_word_index(bit_count - 1))
+ == static_cast<Word_type>(static_cast<Word_type>(get_word_mask(bit_count - 1) << 1)
+ - 1);
+ }
+ constexpr bool any() const noexcept
+ {
+ if(bit_count == 0)
+ return false;
+ for(std::size_t i = 0; i < get_word_index(bit_count - 1); i++)
+ if(get_word(i) != 0)
+ return true;
+ return get_word(get_word_index(bit_count - 1)) != 0;
+ }
+ constexpr bool none() const noexcept
+ {
+ return !any();
+ }
+ constexpr std::size_t count() const noexcept
+ {
+ std::size_t retval = 0;
+ for(std::size_t i = 0; i < word_count; i++)
+ {
+ static_assert(
+ std::numeric_limits<Word_type>::max() <= std::numeric_limits<std::uint64_t>::max(),
+ "");
+ if(std::numeric_limits<Word_type>::max() <= std::numeric_limits<std::uint32_t>::max())
+ retval += popcount32(get_word(i));
+ else
+ retval += popcount64(get_word(i));
+ }
+ return retval;
+ }
+ constexpr std::size_t size() const noexcept
+ {
+ return bit_count;
+ }
+ constexpr bitset &operator&=(const bitset &rt) noexcept
+ {
+ for(std::size_t i = 0; i < word_count; i++)
+ set_word(i, get_word(i) & rt.get_word(i));
+ return *this;
+ }
+ friend constexpr bitset operator&(bitset a, const bitset &b) noexcept
+ {
+ return a &= b;
+ }
+ constexpr bitset &operator|=(const bitset &rt) noexcept
+ {
+ for(std::size_t i = 0; i < word_count; i++)
+ set_word(i, get_word(i) | rt.get_word(i));
+ return *this;
+ }
+ friend constexpr bitset operator|(bitset a, const bitset &b) noexcept
+ {
+ return a |= b;
+ }
+ constexpr bitset &operator^=(const bitset &rt) noexcept
+ {
+ for(std::size_t i = 0; i < word_count; i++)
+ set_word(i, get_word(i) ^ rt.get_word(i));
+ return *this;
+ }
+ friend constexpr bitset operator^(bitset a, const bitset &b) noexcept
+ {
+ return a ^= b;
+ }
+ constexpr bitset &flip() noexcept
+ {
+ for(std::size_t i = 0; i < get_word_index(bit_count - 1); i++)
+ set_word(i, ~get_word(i));
+ set_word(get_word_index(bit_count - 1),
+ get_word(get_word_index(bit_count - 1))
+ ^ static_cast<Word_type>(
+ static_cast<Word_type>(get_word_mask(bit_count - 1) << 1) - 1));
+ return *this;
+ }
+ constexpr bitset operator~() const noexcept
+ {
+ bitset retval = *this;
+ retval.flip();
+ return retval;
+ }
+ constexpr bitset &operator<<=(std::size_t shiftCount) noexcept
+ {
+ if(shiftCount >= bit_count)
+ return reset();
+ std::size_t shiftWord_count = shiftCount / word_bit_count;
+ std::size_t shiftBitCount = shiftCount % word_bit_count;
+ if(shiftBitCount == 0)
+ {
+ for(std::size_t i = word_count; i > 0; i--)
+ {
+ std::size_t index = i - 1;
+ set_word(index, get_word_checked(index - shiftWord_count));
+ }
+ }
+ else
+ {
+ for(std::size_t i = word_count; i > 0; i--)
+ {
+ std::size_t index = i - 1;
+ Word_type highWord = get_word_checked(index - shiftWord_count);
+ Word_type lowWord = get_word_checked(index - 1 - shiftWord_count);
+ set_word(
+ index,
+ (lowWord >> (word_bit_count - shiftBitCount)) | (highWord << shiftBitCount));
+ }
+ }
+ if(word_count != 0)
+ set_word(word_count - 1,
+ get_word(word_count - 1)
+ & static_cast<Word_type>(
+ static_cast<Word_type>(get_word_mask(bit_count - 1) << 1) - 1));
+ return *this;
+ }
+ constexpr bitset operator<<(std::size_t shiftCount) const noexcept
+ {
+ bitset retval = *this;
+ retval <<= shiftCount;
+ return retval;
+ }
+ constexpr bitset &operator>>=(std::size_t shiftCount) noexcept
+ {
+ if(shiftCount >= bit_count)
+ return reset();
+ std::size_t shiftWord_count = shiftCount / word_bit_count;
+ std::size_t shiftBitCount = shiftCount % word_bit_count;
+ if(shiftBitCount == 0)
+ {
+ for(std::size_t index = 0; index < word_count; index++)
+ {
+ set_word(index, get_word_checked(index + shiftWord_count));
+ }
+ }
+ else
+ {
+ for(std::size_t index = 0; index < word_count; index++)
+ {
+ Word_type highWord = get_word_checked(index + 1 + shiftWord_count);
+ Word_type lowWord = get_word_checked(index + shiftWord_count);
+ set_word(
+ index,
+ (lowWord >> shiftBitCount) | (highWord << (word_bit_count - shiftBitCount)));
+ }
+ }
+ return *this;
+ }
+ constexpr bitset operator>>(std::size_t shiftCount) const noexcept
+ {
+ bitset retval = *this;
+ retval >>= shiftCount;
+ return retval;
+ }
+ constexpr bitset &set() noexcept
+ {
+ if(word_count == 0)
+ return *this;
+ for(std::size_t i = 0; i < get_word_index(bit_count - 1); i++)
+ set_word(i, static_cast<Word_type>(-1));
+ set_word(
+ get_word_index(bit_count - 1),
+ static_cast<Word_type>(static_cast<Word_type>(get_word_mask(bit_count - 1) << 1) - 1));
+ return *this;
+ }
+ constexpr bitset &set(std::size_t bit_index, bool value = true)
+ {
+ if(bit_index >= bit_count)
+ throw std::out_of_range("bit_index out of range in bitset::set");
+ operator[](bit_index) = value;
+ return *this;
+ }
+ constexpr bitset &reset() noexcept
+ {
+ for(std::size_t i = 0; i < word_count; i++)
+ set_word(i, 0);
+ return *this;
+ }
+ constexpr bitset &reset(std::size_t bit_index)
+ {
+ if(bit_index >= bit_count)
+ throw std::out_of_range("bit_index out of range in bitset::reset");
+ operator[](bit_index) = false;
+ return *this;
+ }
+ constexpr bitset &flip(std::size_t bit_index)
+ {
+ if(bit_index >= bit_count)
+ throw std::out_of_range("bit_index out of range in bitset::flip");
+ operator[](bit_index).flip();
+ return *this;
+ }
+ constexpr unsigned long long to_ullong() const
+ {
+ unsigned long long retval = 0;
+ constexpr std::size_t ullBitCount = std::numeric_limits<unsigned long long>::digits;
+ for(std::size_t i = 0; i < word_count; i++)
+ {
+ if(i * word_bit_count >= ullBitCount)
+ {
+ if(get_word(i) != 0)
+ throw std::overflow_error("bit set value too large in bitset::to_ullong");
+ }
+ else
+ {
+ auto word = get_word(i);
+ auto shiftedWord = static_cast<unsigned long long>(word) << i * word_bit_count;
+ if((shiftedWord >> i * word_bit_count) != word)
+ throw std::overflow_error("bit set value too large in bitset::to_ullong");
+ retval |= shiftedWord;
+ }
+ }
+ return retval;
+ }
+ constexpr unsigned long to_ulong() const
+ {
+ unsigned long long retval = to_ullong();
+ if(retval > std::numeric_limits<unsigned long>::max())
+ throw std::overflow_error("bit set value too large in bitset::to_ulong");
+ return retval;
+ }
+ static constexpr std::size_t npos = -1; // not in std::bitset
+ constexpr std::size_t find_first(bool value, std::size_t start = 0) const
+ noexcept // not in std::bitset
+ {
+ if(start >= bit_count)
+ return npos;
+ constexpr std::size_t endWordIndex = get_word_index(bit_count - 1);
+ std::size_t startWordIndex = get_word_index(start);
+ auto startWord = get_word(startWordIndex);
+ if(!value)
+ {
+ if(startWordIndex == endWordIndex)
+ startWord ^= static_cast<Word_type>(
+ static_cast<Word_type>(get_word_mask(bit_count - 1) << 1) - 1);
+ else
+ startWord = ~startWord;
+ }
+ auto mask = get_word_mask(start);
+ for(std::size_t retval = start; mask != 0; mask <<= 1, retval++)
+ {
+ if(startWord & mask)
+ return retval;
+ }
+ if(startWordIndex == endWordIndex)
+ return npos;
+ for(std::size_t word_index = startWordIndex + 1; word_index < endWordIndex; word_index++)
+ {
+ auto word = get_word(word_index);
+ if(word == static_cast<Word_type>(value ? 0 : -1))
+ continue;
+ if(!value)
+ word = ~word;
+ mask = 1;
+ std::size_t retval = word_index * word_bit_count;
+ for(; mask != 0; mask <<= 1, retval++)
+ {
+ if(word & mask)
+ break;
+ }
+ return retval;
+ }
+ auto endWord = get_word(endWordIndex);
+ if(!value)
+ endWord ^= static_cast<Word_type>(
+ static_cast<Word_type>(get_word_mask(bit_count - 1) << 1) - 1);
+ if(endWord == 0)
+ return npos;
+ mask = 1;
+ std::size_t retval = endWordIndex * word_bit_count;
+ for(; mask != 0; mask <<= 1, retval++)
+ {
+ if(endWord & mask)
+ break;
+ }
+ return retval;
+ }
+ constexpr std::size_t find_last(bool value, std::size_t start = npos) const
+ noexcept // not in std::bitset
+ {
+ if(bit_count == 0)
+ return npos;
+ if(start >= bit_count)
+ start = bit_count - 1;
+ std::size_t startWordIndex = get_word_index(start);
+ auto startWord = get_word(startWordIndex);
+ if(!value)
+ {
+ if(startWordIndex == get_word_index(bit_count - 1))
+ startWord ^= static_cast<Word_type>(
+ static_cast<Word_type>(get_word_mask(bit_count - 1) << 1) - 1);
+ else
+ startWord = ~startWord;
+ }
+ auto mask = get_word_mask(start);
+ for(std::size_t retval = start; mask != 0; mask >>= 1, retval--)
+ {
+ if(startWord & mask)
+ return retval;
+ }
+ for(std::size_t word_index = startWordIndex - 1, i = 0; i < startWordIndex;
+ word_index--, i++)
+ {
+ auto word = get_word(word_index);
+ if(word == static_cast<Word_type>(value ? 0 : -1))
+ continue;
+ if(!value)
+ word = ~word;
+ mask = get_word_mask(word_bit_count - 1);
+ std::size_t retval = word_index * word_bit_count + (word_bit_count - 1);
+ for(; mask != 0; mask >>= 1, retval--)
+ {
+ if(word & mask)
+ break;
+ }
+ return retval;
+ }
+ return npos;
+ }
+ constexpr std::size_t hash() const noexcept // not in std::bitset
+ {
+ std::size_t retval = 0;
+ for(std::size_t i = 0; i < word_count; i++)
+ {
+ retval ^= get_word(i);
+ }
+ return retval;
+ }
+};
+
+template <std::size_t Bit_count>
+constexpr std::size_t bitset<Bit_count>::npos;
+}
+}
+
+namespace std
+{
+template <std::size_t Bit_count>
+struct hash<vulkan_cpu::util::bitset<Bit_count>>
+{
+ constexpr std::size_t operator()(const vulkan_cpu::util::bitset<Bit_count> &v) const noexcept
+ {
+ return v.hash();
+ }
+};
+}
+
+#endif /* UTIL_BITSET_H_ */