Skip to content

Commit

Permalink
Map B=6 support. (#65)
Browse files Browse the repository at this point in the history
* WIP 64bit map

* Progress

* Fix set headers.

* Progress

* progress

* B6 map/set tests, fix gcc8 build for map tests

* Make literals unsigned in hamts/bits.h

* Fix bitset overflow issue for maps with B=6

* More overflow fixes

* '1 << idx' now returns 32-bit value or 64-bit value depending on B

* Address review issues.

* Meh. I've broken deduction magic.
  • Loading branch information
fedormatantsev authored and arximboldi committed Nov 25, 2018
1 parent 3eb9a0c commit 1c8fdac
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 30 deletions.
66 changes: 52 additions & 14 deletions immer/detail/hamts/bits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,59 @@ namespace immer {
namespace detail {
namespace hamts {

using size_t = std::size_t;
using hash_t = std::size_t;
using bits_t = std::uint32_t;
using bitmap_t = std::uint32_t;
using count_t = std::uint32_t;
using shift_t = std::uint32_t;
using size_t = std::size_t;
using hash_t = std::size_t;

template <bits_t B>
struct get_bitmap_type
{
static_assert(B < 6u, "B > 6 is not supported.");

using type = std::uint32_t;
};

template <>
struct get_bitmap_type<6u>
{
using type = std::uint64_t;
};

template <bits_t B, typename T=count_t>
constexpr T branches = T{1} << B;
constexpr T branches = T{1u} << B;

template <bits_t B, typename T=size_t>
constexpr T mask = branches<B, T> - 1;
constexpr T mask = branches<B, T> - 1u;

template <bits_t B, typename T=count_t>
constexpr T max_depth = (sizeof(hash_t) * 8 + B - 1) / B;
constexpr T max_depth = (sizeof(hash_t) * 8u + B - 1u) / B;

template <bits_t B, typename T=count_t>
constexpr T max_shift = max_depth<B, count_t> * B;

#define IMMER_HAS_BUILTIN_POPCOUNT 1

inline count_t popcount(bitmap_t x)
inline auto popcount_fallback(std::uint32_t x)
{
// More alternatives:
// https://en.wikipedia.org/wiki/Hamming_weight
// http://wm.ite.pl/articles/sse-popcount.html
// http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
x = x - ((x >> 1) & 0x55555555u);
x = (x & 0x33333333u) + ((x >> 2) & 0x33333333u);
return (((x + (x >> 4u)) & 0xF0F0F0Fu) * 0x1010101u) >> 24u;
}

inline auto popcount_fallback(std::uint64_t x)
{
x = x - ((x >> 1) & 0x5555555555555555u);
x = (x & 0x3333333333333333u) + ((x >> 2u) & 0x3333333333333333u);
return (((x + (x >> 4)) & 0x0F0F0F0F0F0F0F0Fu) * 0x0101010101010101u) >> 56u;
}

inline count_t popcount(std::uint32_t x)
{
#if IMMER_HAS_BUILTIN_POPCOUNT
# if defined(_MSC_VER)
Expand All @@ -48,13 +79,20 @@ inline count_t popcount(bitmap_t x)
return __builtin_popcount(x);
# endif
#else
// More alternatives:
// https://en.wikipedia.org/wiki/Hamming_weight
// http://wm.ite.pl/articles/sse-popcount.html
// http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
x = x - ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
return ((x + (x >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
return popcount_fallback(x);
#endif
}

inline count_t popcount(std::uint64_t x)
{
#if IMMER_HAS_BUILTIN_POPCOUNT
# if defined(_MSC_VER)
return __popcnt64(x);
# else
return __builtin_popcountll(x);
# endif
#else
return popcount_fallback(x);
#endif
}

Expand Down
13 changes: 7 additions & 6 deletions immer/detail/hamts/champ.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ template <typename T,
bits_t B>
struct champ
{
static_assert(branches<B> <= sizeof(bitmap_t) * 8, "");

static constexpr auto bits = B;

using node_t = node<T, Hash, Equal, MemoryPolicy, B>;
using bitmap_t = typename get_bitmap_type<B>::type;

static_assert(branches<B> <= sizeof(bitmap_t) * 8, "");

node_t* root;
size_t size;
Expand Down Expand Up @@ -126,7 +127,7 @@ struct champ
auto node = root;
auto hash = Hash{}(k);
for (auto i = count_t{}; i < max_depth<B>; ++i) {
auto bit = 1 << (hash & mask<B>);
auto bit = bitmap_t{1u} << (hash & mask<B>);
if (node->nodemap() & bit) {
auto offset = popcount(node->nodemap() & (bit - 1));
node = node->children() [offset];
Expand Down Expand Up @@ -168,7 +169,7 @@ struct champ
};
} else {
auto idx = (hash & (mask<B> << shift)) >> shift;
auto bit = 1 << idx;
auto bit = bitmap_t{1u} << idx;
if (node->nodemap() & bit) {
auto offset = popcount(node->nodemap() & (bit - 1));
auto result = do_add(node->children() [offset],
Expand Down Expand Up @@ -250,7 +251,7 @@ struct champ
};
} else {
auto idx = (hash & (mask<B> << shift)) >> shift;
auto bit = 1 << idx;
auto bit = bitmap_t{1u} << idx;
if (node->nodemap() & bit) {
auto offset = popcount(node->nodemap() & (bit - 1));
auto result = do_update<Project, Default, Combine>(
Expand Down Expand Up @@ -355,7 +356,7 @@ struct champ
return {};
} else {
auto idx = (hash & (mask<B> << shift)) >> shift;
auto bit = 1 << idx;
auto bit = bitmap_t{1u} << idx;
if (node->nodemap() & bit) {
auto offset = popcount(node->nodemap() & (bit - 1));
auto result = do_sub(node->children() [offset],
Expand Down
5 changes: 3 additions & 2 deletions immer/detail/hamts/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct node
using ownee_t = typename transience::ownee;
using edit_t = typename transience::edit;
using value_t = T;
using bitmap_t = typename get_bitmap_type<B>::type;

enum class kind_t
{
Expand Down Expand Up @@ -220,7 +221,7 @@ struct node
{
assert(n >= 1);
auto p = make_inner_n(n);
p->impl.d.data.inner.nodemap = 1 << idx;
p->impl.d.data.inner.nodemap = bitmap_t{1u} << idx;
p->children()[0] = child;
return p;
}
Expand All @@ -246,7 +247,7 @@ struct node
{
assert(idx1 != idx2);
auto p = make_inner_n(n, 2);
p->impl.d.data.inner.datamap = (1 << idx1) | (1 << idx2);
p->impl.d.data.inner.datamap = (bitmap_t{1u} << idx1) | (bitmap_t{1u} << idx2);
auto assign = [&] (auto&& x1, auto&& x2) {
auto vp = p->values();
try {
Expand Down
6 changes: 3 additions & 3 deletions immer/map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ class map_transient;
*/
template <typename K,
typename T,
typename Hash = std::hash<K>,
typename Equal = std::equal_to<K>,
typename MemoryPolicy = default_memory_policy,
typename Hash = std::hash<K>,
typename Equal = std::equal_to<K>,
typename MemoryPolicy = default_memory_policy,
detail::hamts::bits_t B = default_bits>
class map
{
Expand Down
6 changes: 3 additions & 3 deletions immer/set.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ class set_transient;
*
*/
template <typename T,
typename Hash = std::hash<T>,
typename Equal = std::equal_to<T>,
typename MemoryPolicy = default_memory_policy,
typename Hash = std::hash<T>,
typename Equal = std::equal_to<T>,
typename MemoryPolicy = default_memory_policy,
detail::hamts::bits_t B = default_bits>
class set
{
Expand Down
17 changes: 17 additions & 0 deletions test/map/B6.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// immer: immutable data structures for C++
// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
//
// This software is distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
//

#include <immer/map.hpp>

template <typename K, typename T,
typename Hash = std::hash<K>,
typename Eq = std::equal_to<K>>
using test_map_t = immer::map<K, T, Hash, Eq, immer::default_memory_policy, 6u>;

#define MAP_T test_map_t
#include "generic.ipp"
4 changes: 2 additions & 2 deletions test/map/generic.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ TEST_CASE("at")
CHECK(v.at(0) == 0);
CHECK(v.at(42) == 42);
CHECK(v.at(665) == 665);
CHECK_THROWS_AS(v.at(666), std::out_of_range);
CHECK_THROWS_AS(v.at(1234), std::out_of_range);
CHECK_THROWS_AS(v.at(666), std::out_of_range&);
CHECK_THROWS_AS(v.at(1234), std::out_of_range&);
}

TEST_CASE("find")
Expand Down
17 changes: 17 additions & 0 deletions test/set/B6.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// immer: immutable data structures for C++
// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
//
// This software is distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
//

#include <immer/set.hpp>

template <typename T,
typename Hash = std::hash<T>,
typename Eq = std::equal_to<T>>
using test_set_t = immer::set<T, Hash, Eq, immer::default_memory_policy, 6u>;

#define SET_T test_set_t
#include "generic.ipp"

0 comments on commit 1c8fdac

Please sign in to comment.