Browse Source

update sparsepp and fix emission of rational literal in to-cpp conversion

main
dehnert 8 years ago
parent
commit
15e81f1f16
  1. 53
      resources/3rdparty/sparsepp/README.md
  2. 8
      resources/3rdparty/sparsepp/makefile
  3. 424
      resources/3rdparty/sparsepp/sparsepp.h
  4. 139
      resources/3rdparty/sparsepp/spp_test.cc
  5. 86
      resources/3rdparty/sparsepp/spp_utils.h
  6. 2
      src/storm/storage/expressions/ToCppVisitor.cpp

53
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). 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 ## Usage
As shown in the example above, you need to include the header file: `#include <sparsepp.h>` As shown in the example above, you need to include the header file: `#include <sparsepp.h>`
@ -80,18 +86,47 @@ namespace spp
These classes provide the same interface as std::unordered_map and std::unordered_set, with the following differences: 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: - 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++
```c++ for (auto it = c.begin(); it != c.end(); )
for (auto it = c.begin(); it != c.end(); ) if (it->first % 2 == 1)
if (it->first % 2 == 1) it = c.erase(it);
it = c.erase(it); else
else ++it;
++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. - 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 <sparsepp.h>
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<uint64_t, double, Hash64> 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 ## 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: 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:

8
resources/3rdparty/sparsepp/makefile

@ -7,5 +7,11 @@ test:
./spp_test ./spp_test
spp_test: spp_test.cc sparsepp.h makefile 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

424
resources/3rdparty/sparsepp/sparsepp.h

@ -920,6 +920,12 @@ template<int S, int H> class HashObject; // for Google's benchmark, not in spp n
#define SPP_NOEXCEPT noexcept #define SPP_NOEXCEPT noexcept
#endif #endif
#ifdef SPP_NO_CXX11_CONSTEXPR
#define SPP_CONSTEXPR
#else
#define SPP_CONSTEXPR constexpr
#endif
#define SPP_INLINE #define SPP_INLINE
#ifndef SPP_NAMESPACE #ifndef SPP_NAMESPACE
@ -955,75 +961,109 @@ struct spp_hash<T *>
SPP_INLINE size_t operator()(const T *__v) const SPP_NOEXCEPT 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<size_t>((*(reinterpret_cast<const uintptr_t *>(&__v))) >> shift); return static_cast<size_t>((*(reinterpret_cast<const uintptr_t *>(&__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<size_t>(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 <> template <>
struct spp_hash<bool> : public std::unary_function<bool, size_t> struct spp_hash<bool> : public std::unary_function<bool, size_t>
{ {
SPP_INLINE size_t operator()(bool __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(bool __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
}; };
template <> template <>
struct spp_hash<char> : public std::unary_function<char, size_t> struct spp_hash<char> : public std::unary_function<char, size_t>
{ {
SPP_INLINE size_t operator()(char __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(char __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
}; };
template <> template <>
struct spp_hash<signed char> : public std::unary_function<signed char, size_t> struct spp_hash<signed char> : public std::unary_function<signed char, size_t>
{ {
SPP_INLINE size_t operator()(signed char __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(signed char __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
}; };
template <> template <>
struct spp_hash<unsigned char> : public std::unary_function<unsigned char, size_t> struct spp_hash<unsigned char> : public std::unary_function<unsigned char, size_t>
{ {
SPP_INLINE size_t operator()(unsigned char __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(unsigned char __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
}; };
template <> template <>
struct spp_hash<wchar_t> : public std::unary_function<wchar_t, size_t> struct spp_hash<wchar_t> : public std::unary_function<wchar_t, size_t>
{ {
SPP_INLINE size_t operator()(wchar_t __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(wchar_t __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
}; };
template <> template <>
struct spp_hash<short> : public std::unary_function<short, size_t> struct spp_hash<int16_t> : public std::unary_function<int16_t, size_t>
{ {
SPP_INLINE size_t operator()(short __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(int16_t __v) const SPP_NOEXCEPT
{ return spp_mix_32(static_cast<uint32_t>(__v)); }
}; };
template <> template <>
struct spp_hash<unsigned short> : public std::unary_function<unsigned short, size_t> struct spp_hash<uint16_t> : public std::unary_function<uint16_t, size_t>
{ {
SPP_INLINE size_t operator()(unsigned short __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(uint16_t __v) const SPP_NOEXCEPT
{ return spp_mix_32(static_cast<uint32_t>(__v)); }
}; };
template <> template <>
struct spp_hash<int> : public std::unary_function<int, size_t> struct spp_hash<int32_t> : public std::unary_function<int32_t, size_t>
{ {
SPP_INLINE size_t operator()(int __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(int32_t __v) const SPP_NOEXCEPT
{ return spp_mix_32(static_cast<uint32_t>(__v)); }
}; };
template <> template <>
struct spp_hash<unsigned int> : public std::unary_function<unsigned int, size_t> struct spp_hash<uint32_t> : public std::unary_function<uint32_t, size_t>
{ {
SPP_INLINE size_t operator()(unsigned int __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(uint32_t __v) const SPP_NOEXCEPT
{ return spp_mix_32(static_cast<uint32_t>(__v)); }
}; };
template <> template <>
struct spp_hash<long> : public std::unary_function<long, size_t> struct spp_hash<int64_t> : public std::unary_function<int64_t, size_t>
{ {
SPP_INLINE size_t operator()(long __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(int64_t __v) const SPP_NOEXCEPT
{ return spp_mix_64(static_cast<uint64_t>(__v)); }
}; };
template <> template <>
struct spp_hash<unsigned long> : public std::unary_function<unsigned long, size_t> struct spp_hash<uint64_t> : public std::unary_function<uint64_t, size_t>
{ {
SPP_INLINE size_t operator()(unsigned long __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(uint64_t __v) const SPP_NOEXCEPT
{ return spp_mix_64(static_cast<uint64_t>(__v)); }
}; };
template <> template <>
@ -1033,22 +1073,20 @@ struct spp_hash<float> : public std::unary_function<float, size_t>
{ {
// -0.0 and 0.0 should return same hash // -0.0 and 0.0 should return same hash
uint32_t *as_int = reinterpret_cast<uint32_t *>(&__v); uint32_t *as_int = reinterpret_cast<uint32_t *>(&__v);
return (__v == 0) ? static_cast<size_t>(0) : static_cast<size_t>(*as_int); return (__v == 0) ? static_cast<size_t>(0) : spp_mix_32(*as_int);
} }
}; };
#if 0
// todo: we should not ignore half of the double => see libcxx/include/functional
template <> template <>
struct spp_hash<double> : public std::unary_function<double, size_t> struct spp_hash<double> : public std::unary_function<double, size_t>
{ {
SPP_INLINE size_t operator()(double __v) const SPP_NOEXCEPT SPP_INLINE size_t operator()(double __v) const SPP_NOEXCEPT
{ {
// -0.0 and 0.0 should return same hash // -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<uint64_t *>(&__v);
return (__v == 0) ? static_cast<size_t>(0) : spp_mix_64(*as_int);
} }
}; };
#endif
template <class T, int sz> struct Combiner template <class T, int sz> struct Combiner
{ {
@ -1080,7 +1118,7 @@ inline void hash_combine(std::size_t& seed, T const& v)
combiner(seed, hasher(v)); combiner(seed, hasher(v));
} }
}; }
#endif // spp_utils_h_guard_ #endif // spp_utils_h_guard_
@ -1412,30 +1450,36 @@ namespace sparsehash_internal
// Settings contains parameters for growing and shrinking the table. // Settings contains parameters for growing and shrinking the table.
// It also packages zero-size functor (ie. hasher). // It also packages zero-size functor (ie. hasher).
// //
// It does some munging of the hash value in cases where we think // It does some munging of the hash value for the cases where
// (fear) the original hash function might not be very good. In // the original hash function is not be very good.
// 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).
// --------------------------------------------------------------- // ---------------------------------------------------------------
template<typename Key, typename HashFunc, template<typename Key, typename HashFunc, typename SizeType, int HT_MIN_BUCKETS>
typename SizeType, int HT_MIN_BUCKETS>
class sh_hashtable_settings : public HashFunc class sh_hashtable_settings : public HashFunc
{ {
private: private:
#ifndef SPP_MIX_HASH
template <class T, int sz> struct Mixer
{
inline T operator()(T h) const { return h; }
};
#else
template <class T, int sz> struct Mixer template <class T, int sz> struct Mixer
{ {
inline T operator()(T h) const; inline T operator()(T h) const;
}; };
template <class T> struct Mixer<T, 4> template <class T> struct Mixer<T, 4>
{ {
inline T operator()(T h) const 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 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: public:
typedef Key key_type; typedef Key key_type;
@ -1507,8 +1561,8 @@ namespace sparsehash_internal
// ------------------------------------------------------------ // ------------------------------------------------------------
void set_resizing_parameters(float shrink, float grow) void set_resizing_parameters(float shrink, float grow)
{ {
assert(shrink >= 0.0); assert(shrink >= 0.0f);
assert(grow <= 1.0); assert(grow <= 1.0f);
if (shrink > grow/2.0f) if (shrink > grow/2.0f)
shrink = grow / 2.0f; // otherwise we thrash hashtable size shrink = grow / 2.0f; // otherwise we thrash hashtable size
set_shrink_factor(shrink); set_shrink_factor(shrink);
@ -1724,34 +1778,6 @@ template <class T, class U> struct is_relocatable<std::pair<T, U> > :
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
template <class tabletype>
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 // Our iterator as simple as iterators can be: basically it's just
// the index into our table. Dereference, the only complicated // the index into our table. Dereference, the only complicated
// thing, we punt to the table class. This just goes to show how // 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::value_type value_type;
typedef typename tabletype::difference_type difference_type; typedef typename tabletype::difference_type difference_type;
typedef typename tabletype::size_type size_type; typedef typename tabletype::size_type size_type;
typedef table_element_adaptor<tabletype> reference;
typedef table_element_adaptor<tabletype>* pointer;
explicit table_iterator(tabletype *tbl = 0, size_type p = 0) : explicit table_iterator(tabletype *tbl = 0, size_type p = 0) :
table(tbl), pos(p) 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<tabletype>(table, pos);
}
pointer operator->() { return &(operator*()); }
// Helper function to assert things are ok; eg pos is still in range // Helper function to assert things are ok; eg pos is still in range
void check() const void check() const
{ {
@ -1834,11 +1848,6 @@ public:
return pos - it.pos; return pos - it.pos;
} }
reference operator[](difference_type n) const
{
return *(*this + n); // simple though not totally efficient
}
// Comparisons. // Comparisons.
bool operator==(const iterator& it) const bool operator==(const iterator& it) const
{ {
@ -2306,7 +2315,6 @@ public:
typedef value_type* pointer; typedef value_type* pointer;
typedef const value_type* const_pointer; typedef const value_type* const_pointer;
typedef table_element_adaptor<sparsegroup<T, Alloc> > element_adaptor;
typedef uint8_t size_type; // max # of buckets typedef uint8_t size_type; // max # of buckets
// These are our special iterators, that go over non-empty buckets in a // 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_rend() const { return const_reverse_ne_iterator(ne_cbegin()); }
const_reverse_ne_iterator ne_crend() 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: private:
// T can be std::pair<K, V>, but we need to return std::pair<const K, V> // T can be std::pair<K, V>, but we need to return std::pair<const K, V>
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -2566,16 +2564,6 @@ public:
// We also may want to know how many *used* buckets there are // We also may want to know how many *used* buckets there are
size_type num_nonempty() const { return (size_type)_num_items(); } 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 // TODO(csilvers): make protected + friend
// This is used by sparse_hashtable to get an element from the table // This is used by sparse_hashtable to get an element from the table
// when we know it exists. // when we know it exists.
@ -2587,47 +2575,52 @@ public:
typedef std::pair<mutable_pointer, bool> SetResult; typedef std::pair<mutable_pointer, bool> SetResult;
// returns a reference which can be assigned, so we have to create an entry if not private:
// already there typedef spp_::integral_constant<bool,
// ------------------------------------------------------------------------------- (spp_::is_relocatable<value_type>::value &&
reference mutating_get(Alloc &alloc, size_type i) spp_::is_same<allocator_type,
{ spp_::libc_allocator_with_realloc<mutable_value_type> >::value)>
// fills bucket i before getting realloc_and_memmove_ok;
if (!_bmtest(i))
{
SetResult sr = set(alloc, i, false);
if (!sr.second)
::new (sr.first) mutable_value_type();
return *((pointer)sr.first);
}
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 // ------------------------- memory at *p is uninitialized => need to construct
// return a non-const reference, we need to use the assigner adaptor. void _init_val(mutable_value_type *p, const_reference val)
const_reference operator[](size_type i) const
{ {
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: // ------------------------------------------------ memory at *p is initialized
typedef spp_::integral_constant<bool, void _set_val(mutable_value_type *p, const_reference val)
(spp_::is_relocatable<value_type>::value && {
spp_::is_same<allocator_type, *p = spp_const_mutable_ref(val);
spp_::libc_allocator_with_realloc<mutable_value_type> >::value)> }
realloc_and_memmove_ok;
// Our default allocator - try to merge memory buffers // Our default allocator - try to merge memory buffers
// right now it uses Google's traits, but we should use something like folly::IsRelocatable // 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 // 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 <class Val>
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 //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) for (uint32_t i = num_items; i > offset; --i)
memcpy(_group + i, _group + i-1, sizeof(*_group)); 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 // Create space at _group[offset], without special assumptions about value_type
// and allocator_type, with a default value // and allocator_type, with a default value
// return true if the slot was constructed (i.e. contains a valid mutable_value_type // 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 <class Val>
void _set_aux(Alloc &alloc, size_type offset, Val &val, spp_::false_type)
{ {
uint32_t num_items = _num_items(); uint32_t num_items = _num_items();
uint32_t num_alloc = _sizing(num_items); uint32_t num_alloc = _sizing(num_items);
@ -2659,9 +2654,9 @@ private:
if (num_items < num_alloc) if (num_items < num_alloc)
{ {
// create new object at end and rotate it to position // 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); std::rotate(_group + offset, _group + num_items, _group + num_items + 1);
return true; return;
} }
// This is valid because 0 <= offset <= num_items // This is valid because 0 <= offset <= num_items
@ -2674,57 +2669,37 @@ private:
std::uninitialized_copy(MK_MOVE_IT(_group + offset), std::uninitialized_copy(MK_MOVE_IT(_group + offset),
MK_MOVE_IT(_group + num_items), MK_MOVE_IT(_group + num_items),
p + offset + 1); p + offset + 1);
_init_val(p + offset, val);
_free_group(alloc, num_alloc); _free_group(alloc, num_alloc);
_group = p; _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 <class Val>
void _set(Alloc &alloc, size_type i, size_type offset, Val &val)
{ {
if (erased)
{
// assert(_bme_test(i));
_bme_clear(i);
}
if (!_bmtest(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(); _incr_num_items();
_bmset(i); _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 public:
// 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);
}
// used in _move_from (where we can move the old value instead of copying it // This returns the pointer to the inserted item
// ------------------------------------------------------------------------- // ---------------------------------------------
void move(Alloc &alloc, size_type i, reference val) template <class Val>
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); size_type offset = pos_to_offset(i);
if (!_set(alloc, i, offset, false)) _set(alloc, i, offset, val); // may change _group pointer
::new (&_group[offset]) mutable_value_type(); return (pointer)(_group + offset);
using std::swap;
swap(_group[offset], spp_mutable_ref(val)); // called from _move_from, OK to swap
} }
// We let you see if a bucket is non-empty without retrieving it // We let you see if a bucket is non-empty without retrieving it
@ -3074,7 +3049,6 @@ public:
typedef table_iterator<sparsetable<T, Alloc> > iterator; // defined with index typedef table_iterator<sparsetable<T, Alloc> > iterator; // defined with index
typedef const_table_iterator<sparsetable<T, Alloc> > const_iterator; // defined with index typedef const_table_iterator<sparsetable<T, Alloc> > const_iterator; // defined with index
typedef table_element_adaptor<sparsetable<T, Alloc> > element_adaptor;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator; typedef std::reverse_iterator<iterator> reverse_iterator;
@ -3438,14 +3412,6 @@ public:
return which_group(pos.pos).test(pos_in_group(pos.pos)); 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 // TODO(csilvers): make protected + friend
// This is used by sparse_hashtable to get an element from the table // 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)). // 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)); 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 // Needed for hashtables, gets as a ne_iterator. Crashes for empty bcks
const_ne_iterator get_iter(size_type i) const const_ne_iterator get_iter(size_type i) const
{ {
@ -3524,28 +3466,24 @@ public:
_first_group[current_row].offset_to_pos(current_col)); _first_group[current_row].offset_to_pos(current_col));
} }
// This returns a reference to the inserted item (which is a copy of val) // Val can be reference or const_reference
// The trick is to figure out whether we're replacing or inserting anew // ---------------------------------------
// ---------------------------------------------------------------------- template <class Val>
reference set(size_type i, const_reference val, bool erased = false) reference set(size_type i, Val &val)
{ {
assert(i < _table_size); assert(i < _table_size);
group_type &group = which_group(i); group_type &group = which_group(i);
typename group_type::size_type old_numbuckets = group.num_nonempty(); typename group_type::size_type old_numbuckets = group.num_nonempty();
typename group_type::SetResult sr(group.set(_alloc, pos_in_group(i), erased)); pointer p(group.set(_alloc, pos_in_group(i), val));
if (!sr.second)
::new (sr.first) mutable_value_type(val);
else
*sr.first = spp_const_mutable_ref(val);
_num_buckets += group.num_nonempty() - old_numbuckets; _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 // used in _move_from (where we can move the old value instead of copying it
void move(size_type i, reference val) void move(size_type i, reference val)
{ {
assert(i < _table_size); 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; ++_num_buckets;
} }
@ -3816,7 +3754,7 @@ private:
public: public:
typedef Key key_type; typedef Key key_type;
typedef typename spp::cvt<Value>::type value_type; typedef typename spp::cvt<Value>::type value_type;
typedef HashFcn hasher; typedef HashFcn hasher; // user provided or spp_hash<Key>
typedef EqualKey key_equal; typedef EqualKey key_equal;
typedef Alloc allocator_type; typedef Alloc allocator_type;
@ -4101,7 +4039,7 @@ private:
assert(num_probes < bucket_count() assert(num_probes < bucket_count()
&& "Hashtable is full: an error in key_equal<> or hash<>"); && "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(); settings.inc_num_ht_copies();
} }
@ -4483,7 +4421,8 @@ public:
// INSERTION ROUTINES // INSERTION ROUTINES
private: private:
// Private method used by insert_noresize and find_or_insert. // Private method used by insert_noresize and find_or_insert.
reference _insert_at(const_reference obj, size_type pos, bool erased) template <class T>
reference _insert_at(T& obj, size_type pos, bool erased)
{ {
if (size() >= max_size()) if (size() >= max_size())
{ {
@ -4494,11 +4433,12 @@ private:
assert(num_deleted); assert(num_deleted);
--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 // If you know *this is big enough to hold obj, use this routine
std::pair<iterator, bool> _insert_noresize(const_reference obj) template <class T>
std::pair<iterator, bool> _insert_noresize(T& obj)
{ {
Position pos = _find_position(get_key(obj)); Position pos = _find_position(get_key(obj));
bool already_there = (pos._t == pt_full); bool already_there = (pos._t == pt_full);
@ -4536,17 +4476,13 @@ private:
public: public:
#if 0 && !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) #if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES)
template <class... Args> template <class... Args>
pair<iterator, bool> emplace(Args&&... args) std::pair<iterator, bool> emplace(Args&&... args)
{ {
return rep.emplace_unique(std::forward<Args>(args)...); _resize_delta(1);
} value_type obj(std::forward<Args>(args)...);
return _insert_noresize(obj);
template <class... Args>
iterator emplace_hint(const_iterator p, Args&&... args)
{
return rep.emplace_unique(std::forward<Args>(args)...).first;
} }
#endif #endif
@ -4589,12 +4525,14 @@ public:
{ {
// needed to rehash to make room // needed to rehash to make room
// Since we resized, we can't use pos, so recalculate where to insert. // 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 else
{ {
// no need to rehash, insert right here // 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()) if (grp_pos.test())
@ -5153,6 +5091,20 @@ public:
return it->second; return it->second;
} }
#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES)
template <class... Args>
std::pair<iterator, bool> emplace(Args&&... args)
{
return rep.emplace(std::forward<Args>(args)...);
}
template <class... Args>
iterator emplace_hint(const_iterator , Args&&... args)
{
return rep.emplace(std::forward<Args>(args)...).first;
}
#endif
// Insert // Insert
// ------ // ------
std::pair<iterator, bool> std::pair<iterator, bool>
@ -5496,17 +5448,17 @@ public:
std::pair<iterator, iterator> std::pair<iterator, iterator>
equal_range(const key_type& key) const { return rep.equal_range(key); } 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 <class... Args> template <class... Args>
pair<iterator, bool> emplace(Args&&... args) std::pair<iterator, bool> emplace(Args&&... args)
{ {
return rep.emplace_unique(std::forward<Args>(args)...); return rep.emplace(std::forward<Args>(args)...);
} }
template <class... Args> template <class... Args>
iterator emplace_hint(const_iterator p, Args&&... args) iterator emplace_hint(const_iterator , Args&&... args)
{ {
return rep.emplace_unique(std::forward<Args>(args)...).first; return rep.emplace(std::forward<Args>(args)...).first;
} }
#endif #endif

139
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. // All rights reserved.
// //
// This work is derived from Google's sparsehash library // 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. // Copyright (c) 2010, Google Inc.
// All rights reserved. // All rights reserved.
// //
@ -835,7 +805,7 @@ struct TypeList3
typedef typelist::type1 classname##_type6; \ typedef typelist::type1 classname##_type6; \
typedef typelist::type1 classname##_type7; \ typedef typelist::type1 classname##_type7; \
typedef typelist::type1 classname##_type8; \ typedef typelist::type1 classname##_type8; \
typedef typelist::type1 classname##_type9; typedef typelist::type1 classname##_type9
template<typename C1, typename C2, typename C3, typename C4, typename C5, template<typename C1, typename C2, typename C3, typename C4, typename C5,
typename C6, typename C7, typename C8, typename C9> typename C6, typename C7, typename C8, typename C9>
@ -862,7 +832,7 @@ struct TypeList9
typedef typelist::type7 classname##_type7; \ typedef typelist::type7 classname##_type7; \
typedef typelist::type8 classname##_type8; \ typedef typelist::type8 classname##_type8; \
typedef typelist::type9 classname##_type9; \ typedef typelist::type9 classname##_type9; \
static const int classname##_numtypes = 9; static const int classname##_numtypes = 9
#define TYPED_TEST(superclass, testname) \ #define TYPED_TEST(superclass, testname) \
template<typename TypeParam> \ template<typename TypeParam> \
@ -1171,7 +1141,7 @@ struct Identity
// This is just to avoid memory leaks -- it's a global pointer to // This is just to avoid memory leaks -- it's a global pointer to
// all the memory allocated by UniqueObjectHelper. We'll use it // all the memory allocated by UniqueObjectHelper. We'll use it
// to semi-test sparsetable as well. :-) // to semi-test sparsetable as well. :-)
sparsetable<char*> g_unique_charstar_objects(16); std::vector<char*> g_unique_charstar_objects(16, (char *)0);
// This is an object-generator: pass in an index, and it will return a // This is an object-generator: pass in an index, and it will return a
// unique object of type ItemType. We provide specializations for the // unique object of type ItemType. We provide specializations for the
@ -1190,20 +1160,20 @@ template<> string UniqueObjectHelper(int index)
template<> char* UniqueObjectHelper(int index) template<> char* UniqueObjectHelper(int index)
{ {
// First grow the table if need be. // First grow the table if need be.
sparsetable<char*>::size_type table_size = g_unique_charstar_objects.size(); size_t table_size = g_unique_charstar_objects.size();
while (index >= static_cast<int>(table_size)) { while (index >= static_cast<int>(table_size)) {
assert(table_size * 2 > table_size); // avoid overflow problems assert(table_size * 2 > table_size); // avoid overflow problems
table_size *= 2; table_size *= 2;
} }
if (table_size > g_unique_charstar_objects.size()) if (table_size > g_unique_charstar_objects.size())
g_unique_charstar_objects.resize(table_size); g_unique_charstar_objects.resize(table_size, (char *)0);
if (!g_unique_charstar_objects[static_cast<size_t>(index)]) {
if (!g_unique_charstar_objects.test((size_t)index)) {
char buffer[64]; char buffer[64];
snprintf(buffer, sizeof(buffer), "%d", index); snprintf(buffer, sizeof(buffer), "%d", index);
g_unique_charstar_objects[(size_t)index] = _strdup(buffer); g_unique_charstar_objects[static_cast<size_t>(index)] = _strdup(buffer);
} }
return g_unique_charstar_objects.get((size_t)index); return g_unique_charstar_objects[static_cast<size_t>(index)];
} }
template<> const char* UniqueObjectHelper(int index) { template<> const char* UniqueObjectHelper(int index) {
return UniqueObjectHelper<char*>(index); return UniqueObjectHelper<char*>(index);
@ -1475,6 +1445,8 @@ TYPED_TEST(HashtableIntTest, Typedefs)
(void)dt; (void)dt;
(void)p; (void)p;
(void)cp; (void)cp;
(void)kt;
(void)st;
i = this->ht_.begin(); i = this->ht_.begin();
ci = this->ht_.begin(); ci = this->ht_.begin();
li = this->ht_.begin(0); li = this->ht_.begin(0);
@ -1493,6 +1465,93 @@ TYPED_TEST(HashtableAllTest, NormalIterators)
} }
} }
#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES)
template <class T> struct MyHash;
typedef std::pair<std::string, std::string> StringPair;
template<> struct MyHash<StringPair>
{
size_t operator()(StringPair const& p) const
{
return std::hash<string>()(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<iterations;++i)
{
spp::sparse_hash_map<std::string,MovableOnlyType> m;
m.reserve(static_cast<size_t>(container_size));
char buff[20];
for (int j=0; j<container_size; ++j)
{
sprintf(buff, "%d", j);
m.emplace(buff, MovableOnlyType());
}
}
}
TEST(HashtableTest, Emplace)
{
{
sparse_hash_map<std::string, std::string> 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<StringPair, MyHash<StringPair> > 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<int *, int> ht2;
ht2[&i] = 3;
struct Bogus;
sparse_hash_map<Bogus *, int> ht3;
ht3[(Bogus *)0] = 8;
}
#endif
#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES)
TEST(HashtableTest, ReferenceWrapper)
{
sparse_hash_map<int, std::reference_wrapper<int>> x;
int a = 5;
x.insert(std::make_pair(3, std::ref(a)));
EXPECT_EQ(x.at(3), 5);
}
#endif
TEST(HashtableTest, ModifyViaIterator) TEST(HashtableTest, ModifyViaIterator)
{ {
// This only works for hash-maps, since only they have non-const values. // This only works for hash-maps, since only they have non-const values.

86
resources/3rdparty/sparsepp/spp_utils.h

@ -114,6 +114,12 @@
#define SPP_NOEXCEPT noexcept #define SPP_NOEXCEPT noexcept
#endif #endif
#ifdef SPP_NO_CXX11_CONSTEXPR
#define SPP_CONSTEXPR
#else
#define SPP_CONSTEXPR constexpr
#endif
#define SPP_INLINE #define SPP_INLINE
#ifndef SPP_NAMESPACE #ifndef SPP_NAMESPACE
@ -149,75 +155,109 @@ struct spp_hash<T *>
SPP_INLINE size_t operator()(const T *__v) const SPP_NOEXCEPT 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<size_t>((*(reinterpret_cast<const uintptr_t *>(&__v))) >> shift); return static_cast<size_t>((*(reinterpret_cast<const uintptr_t *>(&__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<size_t>(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 <> template <>
struct spp_hash<bool> : public std::unary_function<bool, size_t> struct spp_hash<bool> : public std::unary_function<bool, size_t>
{ {
SPP_INLINE size_t operator()(bool __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(bool __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
}; };
template <> template <>
struct spp_hash<char> : public std::unary_function<char, size_t> struct spp_hash<char> : public std::unary_function<char, size_t>
{ {
SPP_INLINE size_t operator()(char __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(char __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
}; };
template <> template <>
struct spp_hash<signed char> : public std::unary_function<signed char, size_t> struct spp_hash<signed char> : public std::unary_function<signed char, size_t>
{ {
SPP_INLINE size_t operator()(signed char __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(signed char __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
}; };
template <> template <>
struct spp_hash<unsigned char> : public std::unary_function<unsigned char, size_t> struct spp_hash<unsigned char> : public std::unary_function<unsigned char, size_t>
{ {
SPP_INLINE size_t operator()(unsigned char __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(unsigned char __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
}; };
template <> template <>
struct spp_hash<wchar_t> : public std::unary_function<wchar_t, size_t> struct spp_hash<wchar_t> : public std::unary_function<wchar_t, size_t>
{ {
SPP_INLINE size_t operator()(wchar_t __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(wchar_t __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
}; };
template <> template <>
struct spp_hash<short> : public std::unary_function<short, size_t> struct spp_hash<int16_t> : public std::unary_function<int16_t, size_t>
{ {
SPP_INLINE size_t operator()(short __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(int16_t __v) const SPP_NOEXCEPT
{ return spp_mix_32(static_cast<uint32_t>(__v)); }
}; };
template <> template <>
struct spp_hash<unsigned short> : public std::unary_function<unsigned short, size_t> struct spp_hash<uint16_t> : public std::unary_function<uint16_t, size_t>
{ {
SPP_INLINE size_t operator()(unsigned short __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(uint16_t __v) const SPP_NOEXCEPT
{ return spp_mix_32(static_cast<uint32_t>(__v)); }
}; };
template <> template <>
struct spp_hash<int> : public std::unary_function<int, size_t> struct spp_hash<int32_t> : public std::unary_function<int32_t, size_t>
{ {
SPP_INLINE size_t operator()(int __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(int32_t __v) const SPP_NOEXCEPT
{ return spp_mix_32(static_cast<uint32_t>(__v)); }
}; };
template <> template <>
struct spp_hash<unsigned int> : public std::unary_function<unsigned int, size_t> struct spp_hash<uint32_t> : public std::unary_function<uint32_t, size_t>
{ {
SPP_INLINE size_t operator()(unsigned int __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(uint32_t __v) const SPP_NOEXCEPT
{ return spp_mix_32(static_cast<uint32_t>(__v)); }
}; };
template <> template <>
struct spp_hash<long> : public std::unary_function<long, size_t> struct spp_hash<int64_t> : public std::unary_function<int64_t, size_t>
{ {
SPP_INLINE size_t operator()(long __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(int64_t __v) const SPP_NOEXCEPT
{ return spp_mix_64(static_cast<uint64_t>(__v)); }
}; };
template <> template <>
struct spp_hash<unsigned long> : public std::unary_function<unsigned long, size_t> struct spp_hash<uint64_t> : public std::unary_function<uint64_t, size_t>
{ {
SPP_INLINE size_t operator()(unsigned long __v) const SPP_NOEXCEPT {return static_cast<size_t>(__v);} SPP_INLINE size_t operator()(uint64_t __v) const SPP_NOEXCEPT
{ return spp_mix_64(static_cast<uint64_t>(__v)); }
}; };
template <> template <>
@ -227,22 +267,20 @@ struct spp_hash<float> : public std::unary_function<float, size_t>
{ {
// -0.0 and 0.0 should return same hash // -0.0 and 0.0 should return same hash
uint32_t *as_int = reinterpret_cast<uint32_t *>(&__v); uint32_t *as_int = reinterpret_cast<uint32_t *>(&__v);
return (__v == 0) ? static_cast<size_t>(0) : static_cast<size_t>(*as_int); return (__v == 0) ? static_cast<size_t>(0) : spp_mix_32(*as_int);
} }
}; };
#if 0
// todo: we should not ignore half of the double => see libcxx/include/functional
template <> template <>
struct spp_hash<double> : public std::unary_function<double, size_t> struct spp_hash<double> : public std::unary_function<double, size_t>
{ {
SPP_INLINE size_t operator()(double __v) const SPP_NOEXCEPT SPP_INLINE size_t operator()(double __v) const SPP_NOEXCEPT
{ {
// -0.0 and 0.0 should return same hash // -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<uint64_t *>(&__v);
return (__v == 0) ? static_cast<size_t>(0) : spp_mix_64(*as_int);
} }
}; };
#endif
template <class T, int sz> struct Combiner template <class T, int sz> struct Combiner
{ {
@ -274,7 +312,7 @@ inline void hash_combine(std::size_t& seed, T const& v)
combiner(seed, hasher(v)); combiner(seed, hasher(v));
} }
}; }
#endif // spp_utils_h_guard_ #endif // spp_utils_h_guard_

2
src/storm/storage/expressions/ToCppVisitor.cpp

@ -299,7 +299,7 @@ namespace storm {
ToCppTranslationOptions const& options = boost::any_cast<ToCppTranslationOptions const&>(data); ToCppTranslationOptions const& options = boost::any_cast<ToCppTranslationOptions const&>(data);
switch (options.getMode()) { switch (options.getMode()) {
case ToCppTranslationMode::KeepType: case ToCppTranslationMode::KeepType:
stream << expression.getValue(); stream << expression.getValueAsDouble();
break; break;
case ToCppTranslationMode::CastDouble: case ToCppTranslationMode::CastDouble:
stream << "static_cast<double>(" << expression.getValueAsDouble() << ")"; stream << "static_cast<double>(" << expression.getValueAsDouble() << ")";

|||||||
100:0
Loading…
Cancel
Save