From 15e81f1f16b3611dde337e01e3ae9e109fcd1cd6 Mon Sep 17 00:00:00 2001 From: dehnert Date: Thu, 12 Jan 2017 21:56:32 +0100 Subject: [PATCH] update sparsepp and fix emission of rational literal in to-cpp conversion --- resources/3rdparty/sparsepp/README.md | 53 ++- resources/3rdparty/sparsepp/makefile | 8 +- resources/3rdparty/sparsepp/sparsepp.h | 424 ++++++++---------- resources/3rdparty/sparsepp/spp_test.cc | 139 ++++-- resources/3rdparty/sparsepp/spp_utils.h | 86 +++- .../storage/expressions/ToCppVisitor.cpp | 2 +- 6 files changed, 401 insertions(+), 311 deletions(-) diff --git a/resources/3rdparty/sparsepp/README.md b/resources/3rdparty/sparsepp/README.md index df473bed9..241b116b0 100644 --- a/resources/3rdparty/sparsepp/README.md +++ b/resources/3rdparty/sparsepp/README.md @@ -54,6 +54,12 @@ Since the full Sparsepp implementation is contained in a single header file `spa Optionally, a second header file `spp_utils.h` is provided, which implements only the spp::hash_combine() functionality. This is useful when we want to specify a hash function for a user-defined class in an header file, without including the full `sparsepp.h` header (this is demonstrated in [example 2](#example-2---providing-a-hash-function-for-a-user-defined-class) below). +## Warning - iterator invalidation on erase/insert + +1. erasing elements is likely to invalidate iterators (for example when calling `erase()`) + +2. inserting new elements is likely to invalidate iterators (iterator invalidation can also happen with std::unordered_map if rehashing occurs due to the insertion) + ## Usage As shown in the example above, you need to include the header file: `#include ` @@ -80,18 +86,47 @@ namespace spp These classes provide the same interface as std::unordered_map and std::unordered_set, with the following differences: -- Calls to erase() may invalidate iterators. However, conformant to the C++11 standard, the position and range erase functions return an iterator pointing to the position immediately following the last of the elements erased. This makes it easy to traverse a sparse hash table and delete elements matching a condition. For example to delete odd values: - -```c++ - for (auto it = c.begin(); it != c.end(); ) - if (it->first % 2 == 1) - it = c.erase(it); - else - ++it; -``` +- Calls to `erase()` may invalidate iterators. However, conformant to the C++11 standard, the position and range erase functions return an iterator pointing to the position immediately following the last of the elements erased. This makes it easy to traverse a sparse hash table and delete elements matching a condition. For example to delete odd values: + + ```c++ + for (auto it = c.begin(); it != c.end(); ) + if (it->first % 2 == 1) + it = c.erase(it); + else + ++it; + ``` + + As for std::unordered_map, the order of the elements that are not erased is preserved. - Since items are not grouped into buckets, Bucket APIs have been adapted: `max_bucket_count` is equivalent to `max_size`, and `bucket_count` returns the sparsetable size, which is normally at least twice the number of items inserted into the hash_map. +## Integer keys, and other hash function considerations. + +1. For basic integer types, sparsepp provides a default hash function which does some mixing of the bits of the keys (see [Integer Hashing](http://burtleburtle.net/bob/hash/integer.html)). This prevents a pathological case where inserted keys are sequential (1, 2, 3, 4, ...), and the lookup on non-present keys becomes very slow. + + Of course, the user of sparsepp may provide its own hash function, as shown below: + + ```c++ + #include + + struct Hash64 { + size_t operator()(uint64_t k) const { return (k ^ 14695981039346656037ULL) * 1099511628211ULL; } + }; + + struct Hash32 { + size_t operator()(uint32_t k) const { return (k ^ 2166136261U) * 16777619UL; } + }; + + int main() + { + spp::sparse_hash_map map; + ... + } + + ``` + +2. When the user provides its own hash function, for example when inserting custom classes into a hash map, sometimes the resulting hash keys have similar low order bits and cause many collisions, decreasing the efficiency of the hash map. To address this use case, sparsepp provides an optional 'mixing' of the hash key (see [Integer Hash Function](https://gist.github.com/badboy/6267743) which can be enabled by defining the proprocessor macro: SPP_HASH_MIX. + ## Example 2 - providing a hash function for a user-defined class In order to use a sparse_hash_set or sparse_hash_map, a hash function should be provided. Even though a the hash function can be provided via the HashFcn template parameter, we recommend injecting a specialization of `std::hash` for the class into the "std" namespace. For example: diff --git a/resources/3rdparty/sparsepp/makefile b/resources/3rdparty/sparsepp/makefile index 3443f0e27..eed3e5bca 100644 --- a/resources/3rdparty/sparsepp/makefile +++ b/resources/3rdparty/sparsepp/makefile @@ -7,5 +7,11 @@ test: ./spp_test spp_test: spp_test.cc sparsepp.h makefile - $(CXX) -O2 -std=c++0x -D_CRT_SECURE_NO_WARNINGS spp_test.cc -o spp_test + $(CXX) -O2 -std=c++0x -Wall -pedantic -Wextra -D_XOPEN_SOURCE=700 -D_CRT_SECURE_NO_WARNINGS spp_test.cc -o spp_test + +spp_alloc_test: spp_alloc_test.cc spp_alloc.h spp_bitset.h sparsepp.h makefile + $(CXX) -O2 -DNDEBUG -std=c++11 spp_alloc_test.cc -o spp_alloc_test + +perftest1: perftest1.cc sparsepp.h makefile + $(CXX) -O2 -DNDEBUG -std=c++11 perftest1.cc -o perftest1 diff --git a/resources/3rdparty/sparsepp/sparsepp.h b/resources/3rdparty/sparsepp/sparsepp.h index 5706adb0d..8fc36ce47 100644 --- a/resources/3rdparty/sparsepp/sparsepp.h +++ b/resources/3rdparty/sparsepp/sparsepp.h @@ -920,6 +920,12 @@ template class HashObject; // for Google's benchmark, not in spp n #define SPP_NOEXCEPT noexcept #endif +#ifdef SPP_NO_CXX11_CONSTEXPR + #define SPP_CONSTEXPR +#else + #define SPP_CONSTEXPR constexpr +#endif + #define SPP_INLINE #ifndef SPP_NAMESPACE @@ -955,75 +961,109 @@ struct spp_hash SPP_INLINE size_t operator()(const T *__v) const SPP_NOEXCEPT { - static const size_t shift = spp_log2(1 + sizeof(T)); + static const size_t shift = 3; // spp_log2(1 + sizeof(T)); // T might be incomplete! return static_cast((*(reinterpret_cast(&__v))) >> shift); } }; +// from http://burtleburtle.net/bob/hash/integer.html +// fast and efficient for power of two table sizes where we always +// consider the last bits. +// --------------------------------------------------------------- +inline size_t spp_mix_32(uint32_t a) +{ + a = a ^ (a >> 4); + a = (a ^ 0xdeadbeef) + (a << 5); + a = a ^ (a >> 11); + return static_cast(a); +} + +// Maybe we should do a more thorough scrambling as described in +// https://gist.github.com/badboy/6267743 +// ------------------------------------------------------------- +inline size_t spp_mix_64(uint64_t a) +{ + a = a ^ (a >> 4); + a = (a ^ 0xdeadbeef) + (a << 5); + a = a ^ (a >> 11); + return a; +} + template <> struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(bool __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(bool __v) const SPP_NOEXCEPT + { return static_cast(__v); } }; template <> struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(char __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(char __v) const SPP_NOEXCEPT + { return static_cast(__v); } }; template <> struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(signed char __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(signed char __v) const SPP_NOEXCEPT + { return static_cast(__v); } }; template <> struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(unsigned char __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(unsigned char __v) const SPP_NOEXCEPT + { return static_cast(__v); } }; template <> struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(wchar_t __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(wchar_t __v) const SPP_NOEXCEPT + { return static_cast(__v); } }; template <> -struct spp_hash : public std::unary_function +struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(short __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(int16_t __v) const SPP_NOEXCEPT + { return spp_mix_32(static_cast(__v)); } }; template <> -struct spp_hash : public std::unary_function +struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(unsigned short __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(uint16_t __v) const SPP_NOEXCEPT + { return spp_mix_32(static_cast(__v)); } }; template <> -struct spp_hash : public std::unary_function +struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(int __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(int32_t __v) const SPP_NOEXCEPT + { return spp_mix_32(static_cast(__v)); } }; template <> -struct spp_hash : public std::unary_function +struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(unsigned int __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(uint32_t __v) const SPP_NOEXCEPT + { return spp_mix_32(static_cast(__v)); } }; template <> -struct spp_hash : public std::unary_function +struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(long __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(int64_t __v) const SPP_NOEXCEPT + { return spp_mix_64(static_cast(__v)); } }; template <> -struct spp_hash : public std::unary_function +struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(unsigned long __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(uint64_t __v) const SPP_NOEXCEPT + { return spp_mix_64(static_cast(__v)); } }; template <> @@ -1033,22 +1073,20 @@ struct spp_hash : public std::unary_function { // -0.0 and 0.0 should return same hash uint32_t *as_int = reinterpret_cast(&__v); - return (__v == 0) ? static_cast(0) : static_cast(*as_int); + return (__v == 0) ? static_cast(0) : spp_mix_32(*as_int); } }; -#if 0 -// todo: we should not ignore half of the double => see libcxx/include/functional template <> struct spp_hash : public std::unary_function { SPP_INLINE size_t operator()(double __v) const SPP_NOEXCEPT { // -0.0 and 0.0 should return same hash - return (__v == 0) ? (size_t)0 : (size_t)*((uint64_t *)&__v); + uint64_t *as_int = reinterpret_cast(&__v); + return (__v == 0) ? static_cast(0) : spp_mix_64(*as_int); } }; -#endif template struct Combiner { @@ -1080,7 +1118,7 @@ inline void hash_combine(std::size_t& seed, T const& v) combiner(seed, hasher(v)); } -}; +} #endif // spp_utils_h_guard_ @@ -1412,30 +1450,36 @@ namespace sparsehash_internal // Settings contains parameters for growing and shrinking the table. // It also packages zero-size functor (ie. hasher). // - // It does some munging of the hash value in cases where we think - // (fear) the original hash function might not be very good. In - // particular, the default hash of pointers is the identity hash, - // so probably all the low bits are 0. We identify when we think - // we're hashing a pointer, and chop off the low bits. Note this - // isn't perfect: even when the key is a pointer, we can't tell - // for sure that the hash is the identity hash. If it's not, this - // is needless work (and possibly, though not likely, harmful). + // It does some munging of the hash value for the cases where + // the original hash function is not be very good. // --------------------------------------------------------------- - template + template class sh_hashtable_settings : public HashFunc { private: +#ifndef SPP_MIX_HASH + template struct Mixer + { + inline T operator()(T h) const { return h; } + }; +#else template struct Mixer { inline T operator()(T h) const; }; - template struct Mixer + template struct Mixer { inline T operator()(T h) const { - return h + (h >> 7) + (h >> 13) + (h >> 23); + // from Thomas Wang - https://gist.github.com/badboy/6267743 + // --------------------------------------------------------- + h = (h ^ 61) ^ (h >> 16); + h = h + (h << 3); + h = h ^ (h >> 4); + h = h * 0x27d4eb2d; + h = h ^ (h >> 15); + return h; } }; @@ -1443,9 +1487,19 @@ namespace sparsehash_internal { inline T operator()(T h) const { - return h + (h >> 7) + (h >> 13) + (h >> 23) + (h >> 32); + // from Thomas Wang - https://gist.github.com/badboy/6267743 + // --------------------------------------------------------- + h = (~h) + (h << 21); // h = (h << 21) - h - 1; + h = h ^ (h >> 24); + h = (h + (h << 3)) + (h << 8); // h * 265 + h = h ^ (h >> 14); + h = (h + (h << 2)) + (h << 4); // h * 21 + h = h ^ (h >> 28); + h = h + (h << 31); + return h; } }; +#endif public: typedef Key key_type; @@ -1507,8 +1561,8 @@ namespace sparsehash_internal // ------------------------------------------------------------ void set_resizing_parameters(float shrink, float grow) { - assert(shrink >= 0.0); - assert(grow <= 1.0); + assert(shrink >= 0.0f); + assert(grow <= 1.0f); if (shrink > grow/2.0f) shrink = grow / 2.0f; // otherwise we thrash hashtable size set_shrink_factor(shrink); @@ -1724,34 +1778,6 @@ template struct is_relocatable > : // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- -template -class table_element_adaptor -{ -public: - typedef typename tabletype::value_type value_type; - typedef typename tabletype::size_type size_type; - typedef typename tabletype::reference reference; - typedef typename tabletype::pointer pointer; - - table_element_adaptor(tabletype *tbl, size_type p) : - table(tbl), pos(p) - { } - - table_element_adaptor& operator=(const value_type &val) - { - table->set(pos, val, false); - return *this; - } - - operator value_type() { return table->get(pos); } // we look like a value - - pointer operator& () { return &table->mutating_get(pos); } - -private: - tabletype* table; - size_type pos; -}; - // Our iterator as simple as iterators can be: basically it's just // the index into our table. Dereference, the only complicated // thing, we punt to the table class. This just goes to show how @@ -1774,23 +1800,11 @@ public: typedef typename tabletype::value_type value_type; typedef typename tabletype::difference_type difference_type; typedef typename tabletype::size_type size_type; - typedef table_element_adaptor reference; - typedef table_element_adaptor* pointer; explicit table_iterator(tabletype *tbl = 0, size_type p = 0) : table(tbl), pos(p) { } - // The main thing our iterator does is dereference. If the table entry - // we point to is empty, we return the default value type. - // This is the big different function from the const iterator. - reference operator*() - { - return table_element_adaptor(table, pos); - } - - pointer operator->() { return &(operator*()); } - // Helper function to assert things are ok; eg pos is still in range void check() const { @@ -1834,11 +1848,6 @@ public: return pos - it.pos; } - reference operator[](difference_type n) const - { - return *(*this + n); // simple though not totally efficient - } - // Comparisons. bool operator==(const iterator& it) const { @@ -2306,7 +2315,6 @@ public: typedef value_type* pointer; typedef const value_type* const_pointer; - typedef table_element_adaptor > element_adaptor; typedef uint8_t size_type; // max # of buckets // These are our special iterators, that go over non-empty buckets in a @@ -2332,16 +2340,6 @@ public: const_reverse_ne_iterator ne_rend() const { return const_reverse_ne_iterator(ne_cbegin()); } const_reverse_ne_iterator ne_crend() const { return const_reverse_ne_iterator(ne_cbegin()); } - - // This gives us the "default" value to return for an empty bucket. - // We just use the default constructor on T, the template type - // ---------------------------------------------------------------- - const_reference default_value() const - { - static value_type defaultval = value_type(); - return defaultval; - } - private: // T can be std::pair, but we need to return std::pair // --------------------------------------------------------------------- @@ -2566,16 +2564,6 @@ public: // We also may want to know how many *used* buckets there are size_type num_nonempty() const { return (size_type)_num_items(); } - // get()/set() are explicitly const/non-const. You can use [] if - // you want something that can be either (potentially more expensive). - const_reference get(size_type i) const - { - if (_bmtest(i)) // bucket i is occupied - return (const_reference)_group[pos_to_offset(i)]; - else - return default_value(); // return the default reference - } - // TODO(csilvers): make protected + friend // This is used by sparse_hashtable to get an element from the table // when we know it exists. @@ -2587,47 +2575,52 @@ public: typedef std::pair SetResult; - // returns a reference which can be assigned, so we have to create an entry if not - // already there - // ------------------------------------------------------------------------------- - reference mutating_get(Alloc &alloc, size_type i) - { - // fills bucket i before getting - if (!_bmtest(i)) - { - SetResult sr = set(alloc, i, false); - if (!sr.second) - ::new (sr.first) mutable_value_type(); - return *((pointer)sr.first); - } +private: + typedef spp_::integral_constant::value && + spp_::is_same >::value)> + realloc_and_memmove_ok; - return _group[pos_to_offset(i)]; + // ------------------------- memory at *p is uninitialized => need to construct + void _init_val(mutable_value_type *p, reference val) + { +#if !defined(SPP_NO_CXX11_RVALUE_REFERENCES) + ::new (p) mutable_value_type(std::move(val)); +#else + ::new (p) mutable_value_type(val); +#endif } - // Syntactic sugar. It's easy to return a const reference. To - // return a non-const reference, we need to use the assigner adaptor. - const_reference operator[](size_type i) const + // ------------------------- memory at *p is uninitialized => need to construct + void _init_val(mutable_value_type *p, const_reference val) { - return get(i); + ::new (p) mutable_value_type(val); } - element_adaptor operator[](size_type i) + // ------------------------------------------------ memory at *p is initialized + void _set_val(mutable_value_type *p, reference val) { - return element_adaptor(this, i); +#if !defined(SPP_NO_CXX11_RVALUE_REFERENCES) + *p = std::move(val); +#else + using std::swap; + swap(*p, spp_mutable_ref(val)); +#endif } -private: - typedef spp_::integral_constant::value && - spp_::is_same >::value)> - realloc_and_memmove_ok; + // ------------------------------------------------ memory at *p is initialized + void _set_val(mutable_value_type *p, const_reference val) + { + *p = spp_const_mutable_ref(val); + } // Our default allocator - try to merge memory buffers // right now it uses Google's traits, but we should use something like folly::IsRelocatable // return true if the slot was constructed (i.e. contains a valid mutable_value_type // --------------------------------------------------------------------------------- - bool _set_aux(Alloc &alloc, size_type offset, spp_::true_type) + template + void _set_aux(Alloc &alloc, size_type offset, Val &val, spp_::true_type) { //static int x=0; if (++x < 10) printf("x\n"); // check we are getting here @@ -2643,14 +2636,16 @@ private: for (uint32_t i = num_items; i > offset; --i) memcpy(_group + i, _group + i-1, sizeof(*_group)); - return false; + + _init_val(_group + offset, val); } // Create space at _group[offset], without special assumptions about value_type // and allocator_type, with a default value // return true if the slot was constructed (i.e. contains a valid mutable_value_type // --------------------------------------------------------------------------------- - bool _set_aux(Alloc &alloc, size_type offset, spp_::false_type) + template + void _set_aux(Alloc &alloc, size_type offset, Val &val, spp_::false_type) { uint32_t num_items = _num_items(); uint32_t num_alloc = _sizing(num_items); @@ -2659,9 +2654,9 @@ private: if (num_items < num_alloc) { // create new object at end and rotate it to position - ::new (&_group[num_items]) mutable_value_type(); + _init_val(&_group[num_items], val); std::rotate(_group + offset, _group + num_items, _group + num_items + 1); - return true; + return; } // This is valid because 0 <= offset <= num_items @@ -2674,57 +2669,37 @@ private: std::uninitialized_copy(MK_MOVE_IT(_group + offset), MK_MOVE_IT(_group + num_items), p + offset + 1); + _init_val(p + offset, val); _free_group(alloc, num_alloc); _group = p; - return false; } -public: - - // TODO(austern): Make this exception safe: handle exceptions from - // value_type's copy constructor. - // return true if the slot was constructed (i.e. contains a valid mutable_value_type) // ---------------------------------------------------------------------------------- - bool _set(Alloc &alloc, size_type i, size_type offset, bool erased) + template + void _set(Alloc &alloc, size_type i, size_type offset, Val &val) { - if (erased) - { - // assert(_bme_test(i)); - _bme_clear(i); - } - if (!_bmtest(i)) { - bool res = _set_aux(alloc, offset, realloc_and_memmove_ok()); + _set_aux(alloc, offset, val, realloc_and_memmove_ok()); _incr_num_items(); _bmset(i); - return res; } - return true; + else + _set_val(&_group[offset], val); } - // This returns a pair (first is a pointer to the item's location, second is whether - // that location is constructed (i.e. contains a valid mutable_value_type) - // --------------------------------------------------------------------------------- - SetResult set(Alloc &alloc, size_type i, bool erased) - { - size_type offset = pos_to_offset(i); - bool constructed = _set(alloc, i, offset, erased); // may change _group pointer - return std::make_pair(_group + offset, constructed); - } +public: - // used in _move_from (where we can move the old value instead of copying it - // ------------------------------------------------------------------------- - void move(Alloc &alloc, size_type i, reference val) + // This returns the pointer to the inserted item + // --------------------------------------------- + template + pointer set(Alloc &alloc, size_type i, Val &val) { - // assert(!_bmtest(i)); + _bme_clear(i); // in case this was an "erased" location - size_type offset = pos_to_offset(i); - if (!_set(alloc, i, offset, false)) - ::new (&_group[offset]) mutable_value_type(); - - using std::swap; - swap(_group[offset], spp_mutable_ref(val)); // called from _move_from, OK to swap + size_type offset = pos_to_offset(i); + _set(alloc, i, offset, val); // may change _group pointer + return (pointer)(_group + offset); } // We let you see if a bucket is non-empty without retrieving it @@ -3074,7 +3049,6 @@ public: typedef table_iterator > iterator; // defined with index typedef const_table_iterator > const_iterator; // defined with index - typedef table_element_adaptor > element_adaptor; typedef std::reverse_iterator const_reverse_iterator; typedef std::reverse_iterator reverse_iterator; @@ -3438,14 +3412,6 @@ public: return which_group(pos.pos).test(pos_in_group(pos.pos)); } - // We only return const_references because it's really hard to - // return something settable for empty buckets. Use set() instead. - const_reference get(size_type i) const - { - assert(i < _table_size); - return which_group(i).get(pos_in_group(i)); - } - // TODO(csilvers): make protected + friend // This is used by sparse_hashtable to get an element from the table // when we know it exists (because the caller has called test(i)). @@ -3457,30 +3423,6 @@ public: return which_group(i).unsafe_get(pos_in_group(i)); } - // TODO(csilvers): make protected + friend element_adaptor - reference mutating_get(size_type i) - { - // fills bucket i before getting - assert(i < _table_size); - - GroupsReference grp(which_group(i)); - typename group_type::size_type old_numbuckets = grp.num_nonempty(); - reference retval = grp.mutating_get(_alloc, pos_in_group(i)); - _num_buckets += grp.num_nonempty() - old_numbuckets; - return retval; - } - - // Syntactic sugar. As in sparsegroup, the non-const version is harder - const_reference operator[](size_type i) const - { - return get(i); - } - - element_adaptor operator[](size_type i) - { - return element_adaptor(this, i); - } - // Needed for hashtables, gets as a ne_iterator. Crashes for empty bcks const_ne_iterator get_iter(size_type i) const { @@ -3524,28 +3466,24 @@ public: _first_group[current_row].offset_to_pos(current_col)); } - // This returns a reference to the inserted item (which is a copy of val) - // The trick is to figure out whether we're replacing or inserting anew - // ---------------------------------------------------------------------- - reference set(size_type i, const_reference val, bool erased = false) + // Val can be reference or const_reference + // --------------------------------------- + template + reference set(size_type i, Val &val) { assert(i < _table_size); group_type &group = which_group(i); typename group_type::size_type old_numbuckets = group.num_nonempty(); - typename group_type::SetResult sr(group.set(_alloc, pos_in_group(i), erased)); - if (!sr.second) - ::new (sr.first) mutable_value_type(val); - else - *sr.first = spp_const_mutable_ref(val); + pointer p(group.set(_alloc, pos_in_group(i), val)); _num_buckets += group.num_nonempty() - old_numbuckets; - return *((pointer)sr.first); + return *p; } // used in _move_from (where we can move the old value instead of copying it void move(size_type i, reference val) { assert(i < _table_size); - which_group(i).move(_alloc, pos_in_group(i), val); + which_group(i).set(_alloc, pos_in_group(i), val); ++_num_buckets; } @@ -3816,7 +3754,7 @@ private: public: typedef Key key_type; typedef typename spp::cvt::type value_type; - typedef HashFcn hasher; + typedef HashFcn hasher; // user provided or spp_hash typedef EqualKey key_equal; typedef Alloc allocator_type; @@ -4101,7 +4039,7 @@ private: assert(num_probes < bucket_count() && "Hashtable is full: an error in key_equal<> or hash<>"); } - table.set(bucknum, *it, false); // copies the value to here + table.set(bucknum, *it); // copies the value to here } settings.inc_num_ht_copies(); } @@ -4483,7 +4421,8 @@ public: // INSERTION ROUTINES private: // Private method used by insert_noresize and find_or_insert. - reference _insert_at(const_reference obj, size_type pos, bool erased) + template + reference _insert_at(T& obj, size_type pos, bool erased) { if (size() >= max_size()) { @@ -4494,11 +4433,12 @@ private: assert(num_deleted); --num_deleted; } - return table.set(pos, obj, erased); + return table.set(pos, obj); } // If you know *this is big enough to hold obj, use this routine - std::pair _insert_noresize(const_reference obj) + template + std::pair _insert_noresize(T& obj) { Position pos = _find_position(get_key(obj)); bool already_there = (pos._t == pt_full); @@ -4536,17 +4476,13 @@ private: public: -#if 0 && !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) template - pair emplace(Args&&... args) + std::pair emplace(Args&&... args) { - return rep.emplace_unique(std::forward(args)...); - } - - template - iterator emplace_hint(const_iterator p, Args&&... args) - { - return rep.emplace_unique(std::forward(args)...).first; + _resize_delta(1); + value_type obj(std::forward(args)...); + return _insert_noresize(obj); } #endif @@ -4589,12 +4525,14 @@ public: { // needed to rehash to make room // Since we resized, we can't use pos, so recalculate where to insert. - return *(_insert_noresize(default_value(key)).first); + value_type def(default_value(key)); + return *(_insert_noresize(def).first); } else { // no need to rehash, insert right here - return _insert_at(default_value(key), erased ? erased_pos : bucknum, erased); + value_type def(default_value(key)); + return _insert_at(def, erased ? erased_pos : bucknum, erased); } } if (grp_pos.test()) @@ -5153,6 +5091,20 @@ public: return it->second; } +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) + template + std::pair emplace(Args&&... args) + { + return rep.emplace(std::forward(args)...); + } + + template + iterator emplace_hint(const_iterator , Args&&... args) + { + return rep.emplace(std::forward(args)...).first; + } +#endif + // Insert // ------ std::pair @@ -5496,17 +5448,17 @@ public: std::pair equal_range(const key_type& key) const { return rep.equal_range(key); } -#if 0 && !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) template - pair emplace(Args&&... args) + std::pair emplace(Args&&... args) { - return rep.emplace_unique(std::forward(args)...); + return rep.emplace(std::forward(args)...); } template - iterator emplace_hint(const_iterator p, Args&&... args) + iterator emplace_hint(const_iterator , Args&&... args) { - return rep.emplace_unique(std::forward(args)...).first; + return rep.emplace(std::forward(args)...).first; } #endif diff --git a/resources/3rdparty/sparsepp/spp_test.cc b/resources/3rdparty/sparsepp/spp_test.cc index 281db9154..e17eb0f82 100644 --- a/resources/3rdparty/sparsepp/spp_test.cc +++ b/resources/3rdparty/sparsepp/spp_test.cc @@ -1,39 +1,9 @@ // ---------------------------------------------------------------------- -// Copyright (c) 2016, Steven Gregory Popovitch - greg7mdp@gmail.com +// Copyright (c) 2016, Gregory Popovitch - greg7mdp@gmail.com // All rights reserved. // // This work is derived from Google's sparsehash library -// (see https://github.com/sparsehash/sparsehash) whose copyright appears -// below this one. // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * The name of Steven Gregory Popovitch may not be used to -// endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// ---------------------------------------------------------------------- - -// ---------------------------------------------------------------------- // Copyright (c) 2010, Google Inc. // All rights reserved. // @@ -835,7 +805,7 @@ struct TypeList3 typedef typelist::type1 classname##_type6; \ typedef typelist::type1 classname##_type7; \ typedef typelist::type1 classname##_type8; \ - typedef typelist::type1 classname##_type9; + typedef typelist::type1 classname##_type9 template @@ -862,7 +832,7 @@ struct TypeList9 typedef typelist::type7 classname##_type7; \ typedef typelist::type8 classname##_type8; \ typedef typelist::type9 classname##_type9; \ - static const int classname##_numtypes = 9; + static const int classname##_numtypes = 9 #define TYPED_TEST(superclass, testname) \ template \ @@ -1171,7 +1141,7 @@ struct Identity // This is just to avoid memory leaks -- it's a global pointer to // all the memory allocated by UniqueObjectHelper. We'll use it // to semi-test sparsetable as well. :-) -sparsetable g_unique_charstar_objects(16); +std::vector g_unique_charstar_objects(16, (char *)0); // This is an object-generator: pass in an index, and it will return a // unique object of type ItemType. We provide specializations for the @@ -1190,20 +1160,20 @@ template<> string UniqueObjectHelper(int index) template<> char* UniqueObjectHelper(int index) { // First grow the table if need be. - sparsetable::size_type table_size = g_unique_charstar_objects.size(); + size_t table_size = g_unique_charstar_objects.size(); while (index >= static_cast(table_size)) { assert(table_size * 2 > table_size); // avoid overflow problems table_size *= 2; } if (table_size > g_unique_charstar_objects.size()) - g_unique_charstar_objects.resize(table_size); - - if (!g_unique_charstar_objects.test((size_t)index)) { + g_unique_charstar_objects.resize(table_size, (char *)0); + + if (!g_unique_charstar_objects[static_cast(index)]) { char buffer[64]; snprintf(buffer, sizeof(buffer), "%d", index); - g_unique_charstar_objects[(size_t)index] = _strdup(buffer); + g_unique_charstar_objects[static_cast(index)] = _strdup(buffer); } - return g_unique_charstar_objects.get((size_t)index); + return g_unique_charstar_objects[static_cast(index)]; } template<> const char* UniqueObjectHelper(int index) { return UniqueObjectHelper(index); @@ -1475,6 +1445,8 @@ TYPED_TEST(HashtableIntTest, Typedefs) (void)dt; (void)p; (void)cp; + (void)kt; + (void)st; i = this->ht_.begin(); ci = this->ht_.begin(); li = this->ht_.begin(0); @@ -1493,6 +1465,93 @@ TYPED_TEST(HashtableAllTest, NormalIterators) } } + +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) + +template struct MyHash; +typedef std::pair StringPair; + +template<> struct MyHash +{ + size_t operator()(StringPair const& p) const + { + return std::hash()(p.first); + } +}; + +class MovableOnlyType +{ + std::string _str; + std::uint64_t _int; + +public: + // Make object movable and non-copyable + MovableOnlyType(MovableOnlyType &&) = default; + MovableOnlyType(const MovableOnlyType &) = delete; + MovableOnlyType& operator=(MovableOnlyType &&) = default; + MovableOnlyType& operator=(const MovableOnlyType &) = delete; + MovableOnlyType() : _str("whatever"), _int(2) {} +}; + +void movable_emplace_test(std::size_t iterations, int container_size) +{ + for (std::size_t i=0;i m; + m.reserve(static_cast(container_size)); + char buff[20]; + for (int j=0; j mymap; + + mymap.emplace ("NCC-1701", "J.T. Kirk"); + mymap.emplace ("NCC-1701-D", "J.L. Picard"); + mymap.emplace ("NCC-74656", "K. Janeway"); + EXPECT_TRUE(mymap["NCC-74656"] == std::string("K. Janeway")); + + sparse_hash_set > myset; + myset.emplace ("NCC-1701", "J.T. Kirk"); + } + + movable_emplace_test(10, 50); +} +#endif + + +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) +TEST(HashtableTest, IncompleteTypes) +{ + int i; + sparse_hash_map ht2; + ht2[&i] = 3; + + struct Bogus; + sparse_hash_map ht3; + ht3[(Bogus *)0] = 8; +} +#endif + + +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) +TEST(HashtableTest, ReferenceWrapper) +{ + sparse_hash_map> x; + int a = 5; + x.insert(std::make_pair(3, std::ref(a))); + EXPECT_EQ(x.at(3), 5); +} +#endif + + TEST(HashtableTest, ModifyViaIterator) { // This only works for hash-maps, since only they have non-const values. diff --git a/resources/3rdparty/sparsepp/spp_utils.h b/resources/3rdparty/sparsepp/spp_utils.h index 6b627233c..96a8f5bf3 100644 --- a/resources/3rdparty/sparsepp/spp_utils.h +++ b/resources/3rdparty/sparsepp/spp_utils.h @@ -114,6 +114,12 @@ #define SPP_NOEXCEPT noexcept #endif +#ifdef SPP_NO_CXX11_CONSTEXPR + #define SPP_CONSTEXPR +#else + #define SPP_CONSTEXPR constexpr +#endif + #define SPP_INLINE #ifndef SPP_NAMESPACE @@ -149,75 +155,109 @@ struct spp_hash SPP_INLINE size_t operator()(const T *__v) const SPP_NOEXCEPT { - static const size_t shift = spp_log2(1 + sizeof(T)); + static const size_t shift = 3; // spp_log2(1 + sizeof(T)); // T might be incomplete! return static_cast((*(reinterpret_cast(&__v))) >> shift); } }; +// from http://burtleburtle.net/bob/hash/integer.html +// fast and efficient for power of two table sizes where we always +// consider the last bits. +// --------------------------------------------------------------- +inline size_t spp_mix_32(uint32_t a) +{ + a = a ^ (a >> 4); + a = (a ^ 0xdeadbeef) + (a << 5); + a = a ^ (a >> 11); + return static_cast(a); +} + +// Maybe we should do a more thorough scrambling as described in +// https://gist.github.com/badboy/6267743 +// ------------------------------------------------------------- +inline size_t spp_mix_64(uint64_t a) +{ + a = a ^ (a >> 4); + a = (a ^ 0xdeadbeef) + (a << 5); + a = a ^ (a >> 11); + return a; +} + template <> struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(bool __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(bool __v) const SPP_NOEXCEPT + { return static_cast(__v); } }; template <> struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(char __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(char __v) const SPP_NOEXCEPT + { return static_cast(__v); } }; template <> struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(signed char __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(signed char __v) const SPP_NOEXCEPT + { return static_cast(__v); } }; template <> struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(unsigned char __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(unsigned char __v) const SPP_NOEXCEPT + { return static_cast(__v); } }; template <> struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(wchar_t __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(wchar_t __v) const SPP_NOEXCEPT + { return static_cast(__v); } }; template <> -struct spp_hash : public std::unary_function +struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(short __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(int16_t __v) const SPP_NOEXCEPT + { return spp_mix_32(static_cast(__v)); } }; template <> -struct spp_hash : public std::unary_function +struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(unsigned short __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(uint16_t __v) const SPP_NOEXCEPT + { return spp_mix_32(static_cast(__v)); } }; template <> -struct spp_hash : public std::unary_function +struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(int __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(int32_t __v) const SPP_NOEXCEPT + { return spp_mix_32(static_cast(__v)); } }; template <> -struct spp_hash : public std::unary_function +struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(unsigned int __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(uint32_t __v) const SPP_NOEXCEPT + { return spp_mix_32(static_cast(__v)); } }; template <> -struct spp_hash : public std::unary_function +struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(long __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(int64_t __v) const SPP_NOEXCEPT + { return spp_mix_64(static_cast(__v)); } }; template <> -struct spp_hash : public std::unary_function +struct spp_hash : public std::unary_function { - SPP_INLINE size_t operator()(unsigned long __v) const SPP_NOEXCEPT {return static_cast(__v);} + SPP_INLINE size_t operator()(uint64_t __v) const SPP_NOEXCEPT + { return spp_mix_64(static_cast(__v)); } }; template <> @@ -227,22 +267,20 @@ struct spp_hash : public std::unary_function { // -0.0 and 0.0 should return same hash uint32_t *as_int = reinterpret_cast(&__v); - return (__v == 0) ? static_cast(0) : static_cast(*as_int); + return (__v == 0) ? static_cast(0) : spp_mix_32(*as_int); } }; -#if 0 -// todo: we should not ignore half of the double => see libcxx/include/functional template <> struct spp_hash : public std::unary_function { SPP_INLINE size_t operator()(double __v) const SPP_NOEXCEPT { // -0.0 and 0.0 should return same hash - return (__v == 0) ? (size_t)0 : (size_t)*((uint64_t *)&__v); + uint64_t *as_int = reinterpret_cast(&__v); + return (__v == 0) ? static_cast(0) : spp_mix_64(*as_int); } }; -#endif template struct Combiner { @@ -274,7 +312,7 @@ inline void hash_combine(std::size_t& seed, T const& v) combiner(seed, hasher(v)); } -}; +} #endif // spp_utils_h_guard_ diff --git a/src/storm/storage/expressions/ToCppVisitor.cpp b/src/storm/storage/expressions/ToCppVisitor.cpp index 717317d0e..40e75b152 100644 --- a/src/storm/storage/expressions/ToCppVisitor.cpp +++ b/src/storm/storage/expressions/ToCppVisitor.cpp @@ -299,7 +299,7 @@ namespace storm { ToCppTranslationOptions const& options = boost::any_cast(data); switch (options.getMode()) { case ToCppTranslationMode::KeepType: - stream << expression.getValue(); + stream << expression.getValueAsDouble(); break; case ToCppTranslationMode::CastDouble: stream << "static_cast(" << expression.getValueAsDouble() << ")";