Commit 17548296 authored by Mark Olesen's avatar Mark Olesen
Browse files

Switched from old hashing functions to use Bob Jenkins' hash routine

- If the underlying type is contiguous, FixedList hashes its storage directly.
- Drop labelPairHash (non-commutative) from fvMeshDistribute since
  FixedList::Hash does the right thing anyhow.
- Hash<edge> specialization is commutative, without multiplication.
- Hash<triFace> specialization kept multiplication (but now uLabel).
  There's not much point optimizing it, since it's not used much anyhow.

Misc. changes

- added StaticAssert to NamedEnum.H
- label.H / uLabel.H : define FOAM_LABEL_MAX, FOAM_ULABEL_MAX with the
  values finally used for the storage.  These can be useful for pre-processor
  checks elsewhere (although I stopped needing them in the meantime).
parent 44a86232
......@@ -56,6 +56,12 @@ int main(int argc, char *argv[])
Info<< "list:" << list
<< " hash:" << FixedList<label, 4>::Hash<>()(list) << endl;
Info<< "FixedList<label, ..> is contiguous, "
"thus hashing function is irrelevant: with string::hash" << endl;
Info<< "list:" << list
<< " hash:" << FixedList<label, 4>::Hash<string::hash>()(list) << endl;
label a[4] = {0, 1, 2, 3};
FixedList<label, 4> list2(a);
......
......@@ -37,7 +37,7 @@ Description
#include "labelList.H"
#include "labelPair.H"
#include "Hashing.H"
#include "Hash.H"
using namespace Foam;
......@@ -65,64 +65,40 @@ int main(int argc, char *argv[])
forAll(lst, i)
{
unsigned hash1 = Hashing::jenkins(lst[i]);
unsigned hash2 = string::hash()(lst[i]);
unsigned hash1 = string::hash()(lst[i]);
Info<< hex << hash1
<< " (prev " << hash2 << ")"
<< ": " << lst[i] << endl;
Info<< hex << hash1 << ": " << lst[i] << endl;
}
}
else if (listType == "labelList")
{
Info<<"contiguous = " << contiguous<label>() << " "
<< "sizeof(label) " << unsigned(sizeof(label)) << endl << endl;
Info<<"contiguous = " << contiguous<label>() << endl << endl;
labelList lst(is);
unsigned hash4 = 0;
forAll(lst, i)
{
unsigned hash1 = Hashing::intHash(lst[i]);
unsigned hash2 = Hashing::jenkins
(
reinterpret_cast<const char*>(&lst[i]),
sizeof(label)
);
// direct value
unsigned hash1 = Hash<label>()(lst[i]);
unsigned hash3 = Hashing::jenkins
(
reinterpret_cast<const unsigned*>(&lst[i]),
1
);
// incremental
hash4 = Hashing::jenkins
(
reinterpret_cast<const char*>(&lst[i]),
sizeof(label),
hash4
);
// hashed byte-wise
unsigned hash2 = Hash<label>()(lst[i], 0);
Info<< hex << hash1
<< " (alt: " << hash2 << ")"
<< " (alt: " << hash3 << ")"
<< " (incr: " << hash4 << ")"
<< " (seeded: " << hash2 << ")"
<< ": " << dec << lst[i] << endl;
}
if (contiguous<label>())
{
unsigned hash1 = Hashing::jenkins
unsigned hash3 = Hasher
(
lst.cdata(),
lst.size() * sizeof(label)
);
Info<<"contiguous hashed value " << hex << hash1 << endl;
Info<<"contiguous hashed value " << hex << hash3 << endl;
}
}
else if (listType == "labelListList")
......@@ -131,9 +107,9 @@ int main(int argc, char *argv[])
forAll(lst, i)
{
unsigned hash1 = Hashing::jenkins
unsigned hash1 = Hasher
(
reinterpret_cast<const char*>(lst[i].cdata()),
lst[i].cdata(),
lst[i].size() * sizeof(label)
);
......
// code taken more-or-less from Paul Hsieh's tests
#include "Hashing.H"
#include "Hasher.H"
#include <stdio.h>
#include <time.h>
......@@ -692,7 +692,7 @@ uint32_t hashLookup3Orig (const char * k, int length) {
}
uint32_t hashLookup3 (const char * k, int length) {
return Foam::Hashing::jenkins(k, length);
return Foam::Hasher(k, length, 0);
}
......
......@@ -52,7 +52,7 @@ $(strings)/fileName/fileNameIO.C
$(strings)/keyType/keyTypeIO.C
$(strings)/wordRe/wordReIO.C
primitives/hashes/Hashing/Hashing.C
primitives/hashes/Hasher/Hasher.C
sha1 = primitives/hashes/SHA1
$(sha1)/SHA1.C
......
......@@ -82,8 +82,8 @@ class FixedList
public:
//- Hashing function class
// Rotating hash from http://burtleburtle.net/bob/hash/doobs.html
//- Hashing function class.
// Use Hasher directly for contiguous data. Otherwise hash incrementally.
template< class HashT=Hash<T> >
class Hash
{
......@@ -91,7 +91,11 @@ public:
Hash()
{}
inline unsigned operator()(const FixedList<T, Size>&) const;
inline unsigned operator()
(
const FixedList<T, Size>&,
unsigned seed = 0
) const;
};
// Static Member Functions
......
......@@ -403,25 +403,31 @@ inline bool Foam::FixedList<T, Size>::empty() const
#ifndef __CINT__
// Rotating Hash
template<class T, unsigned Size>
template<class HashT>
inline unsigned Foam::FixedList<T, Size>::Hash<HashT>::operator()
(
const FixedList<T, Size>& lst
const FixedList<T, Size>& lst,
unsigned seed
) const
{
static const unsigned farbit(8*sizeof(label)-4);
unsigned val = Size;
for (register unsigned i=0; i<Size; i++)
if (contiguous<T>())
{
val = (val << 4) ^ (val >> farbit) ^ HashT()(lst[i]);
// hash directly
return Hasher(lst.v_, sizeof(lst.v_), seed);
}
else
{
// hash incrementally
unsigned val = seed;
for (register unsigned i=0; i<Size; i++)
{
val = HashT()(lst[i], val);
}
return val;
return val;
}
}
#endif // __CINT__
......
......@@ -37,6 +37,7 @@ SourceFiles
#define NamedEnum_H
#include "HashTable.H"
#include "StaticAssert.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
......@@ -52,6 +53,9 @@ class NamedEnum
:
public HashTable<int>
{
//- nEnum must be positive (non-zero)
StaticAssert(nEnum > 0);
// Private Member Functions
//- Disallow default bitwise copy construct
......@@ -99,7 +103,7 @@ public:
return Enum(HashTable<int>::operator[](name));
}
//- Return the name or the given enumeration element
//- Return the name of the given enumeration element
const char* operator[](const Enum e) const
{
return names[e];
......
......@@ -133,14 +133,37 @@ public:
};
//- Hash specialization for hashing edges
// Simple commutative hash.
//- Hash specialization for hashing edges - a commutative hash value.
// Hash incrementally.
template<>
inline unsigned Hash<edge>::operator()(const edge& e, unsigned seed) const
{
unsigned val = seed;
if (e[0] < e[1])
{
val = Hash<label>()(e[0], val);
val = Hash<label>()(e[1], val);
}
else
{
val = Hash<label>()(e[1], val);
val = Hash<label>()(e[0], val);
}
return val;
}
//- Hash specialization for hashing edges - a commutative hash value.
// Hash incrementally.
template<>
inline unsigned Hash<edge>::operator()(const edge& e) const
{
return (e[0]*e[1] + e[0]+e[1]);
return Hash<edge>()(e, 0);
}
template<>
inline bool contiguous<edge>() {return true;}
......
......@@ -164,14 +164,31 @@ public:
};
//- Hash specialization for hashing triFace
// Simple commutative hash.
//- Hash specialization for hashing triFace - a commutative hash value.
// Hash incrementally.
template<>
inline unsigned Hash<triFace>::operator()(const triFace& t, unsigned seed) const
{
// Fortunately we don't need this very often
const uLabel t0(t[0]);
const uLabel t1(t[1]);
const uLabel t2(t[2]);
const uLabel val = (t0*t1*t2 + t0+t1+t2);
return Hash<uLabel>()(val, seed);
}
//- Hash specialization for hashing triFace - a commutative hash value.
// Hash incrementally.
template<>
inline unsigned Hash<triFace>::operator()(const triFace& t) const
{
return (t[0]*t[1]*t[2] + t[0]+t[1]+t[2]);
return Hash<triFace>::operator()(t, 0);
}
template<>
inline bool contiguous<triFace>() {return true;}
......
......@@ -133,7 +133,7 @@ inline Foam::pointField Foam::triFace::points(const pointField& points) const
inline Foam::face Foam::triFace::triFaceFace() const
{
face f(3);
Foam::face f(3);
f[0] = operator[](0);
f[1] = operator[](1);
......
......@@ -59,7 +59,7 @@ public:
// Constructors
//- Null constructor for lists
//- Null constructor
inline Pair()
{}
......@@ -135,6 +135,27 @@ public:
}
//- compare Pairs
// - 0: different
// - +1: identical
// - -1: same pair, but reversed order
static inline int compare(const Pair<Type>& a, const Pair<Type>& b)
{
if (a[0] == b[0] && a[1] == b[1])
{
return 1;
}
else if (a[0] == b[1] && a[1] == b[0])
{
return -1;
}
else
{
return 0;
}
}
// Friend Operators
friend bool operator==(const Pair<Type>& a, const Pair<Type>& b)
......
......@@ -34,8 +34,8 @@ namespace Foam
const char* const pTraits<Scalar>::typeName = "scalar";
const Scalar pTraits<Scalar>::zero = 0.0;
const Scalar pTraits<Scalar>::one = 1.0;
const Scalar pTraits<Scalar>::max = ScalarVGREAT;
const Scalar pTraits<Scalar>::min = -ScalarVGREAT;
const Scalar pTraits<Scalar>::max = ScalarVGREAT;
const char* pTraits<Scalar>::componentNames[] = { "x" };
......@@ -47,11 +47,11 @@ pTraits<Scalar>::pTraits(Istream& is)
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
word name(const Scalar s)
word name(const Scalar val)
{
std::ostringstream osBuffer;
osBuffer << s;
return osBuffer.str();
std::ostringstream buf;
buf << val;
return buf.str();
}
......
......@@ -39,7 +39,7 @@ Description
Foam::Istream& Foam::operator>>(Istream& is, char& c)
{
is.read(c);
is.check("Istream& operator>>(Istream& is, char& c)");
is.check("Istream& operator>>(Istream&, char&)");
return is;
}
......
......@@ -27,7 +27,7 @@ Class
Description
Hash function class for primitives. All non-primitives used to hash
entries on hash tables need a specialized version of this class.
entries on hash tables likely need a specialized version of this class.
\*---------------------------------------------------------------------------*/
......@@ -36,6 +36,7 @@ Description
#include "label.H"
#include "uLabel.H"
#include "Hasher.H"
#include "pTraits.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
......@@ -50,15 +51,19 @@ namespace Foam
template<class PrimitiveType>
class Hash
{
public:
Hash()
{}
unsigned operator()(const PrimitiveType& p, unsigned seed) const
{
return Hasher(&p, sizeof(p), seed);
}
unsigned operator()(const PrimitiveType& p) const
{
return unsigned(p);
return Hasher(&p, sizeof(p));
}
};
......@@ -66,19 +71,53 @@ public:
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//- Hash specialization for hashing pointer addresses
//- Hash specialization for hashing pointer addresses.
// Treat a pointer like a long.
// This should work for both 32-bit and 64-bit pointers.
template<>
class Hash<void*>
{
public:
Hash()
{}
unsigned operator()(const void* const& p, unsigned seed) const
{
return Hash<long>()(long(p), seed);
}
unsigned operator()(const void* const& p) const
{
return long(p);
return Hash<long>()(long(p));
}
};
//- Hash specialization for hashing labels
template<>
class Hash<Foam::label>
{
public:
Hash()
{}
//- Incrementally hash a label.
// This will necessarily return a different value than the
// non-incremental version.
unsigned operator()(const label& p, unsigned seed) const
{
return Hasher(&p, sizeof(label), seed);
}
//- Return the unsigned representation of a label.
// This helps if people have relied on the hash value corresponding to
// the natural order.
unsigned operator()(const label& p) const
{
return p;
}
};
......
......@@ -26,14 +26,16 @@ Description
Hashing functions, mostly from Bob Jenkins
\*---------------------------------------------------------------------------*/
#include "Hashing.H"
#include <cstring>
#include "Hasher.H"
#if defined (__GLIBC__)
# include <endian.h>
#endif
// Left-rotate a 32-bit value and carry by nBits
#define bitRotateLeft(x, nBits) (((x) << (nBits)) | ((x) >> (32 - (nBits))))
// ----------------------------------------------------------------------------
// lookup3.c, by Bob Jenkins, May 2006, Public Domain.
//
......@@ -68,7 +70,6 @@ Description
// on 1 byte), but shoehorning those bytes into integers efficiently is messy.
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// mix -- mix 3 32-bit values reversibly.
//
......@@ -112,6 +113,17 @@ Description
// rotates.
// ----------------------------------------------------------------------------
#define bitMixer(a, b, c) \
{ \
a -= c; a ^= bitRotateLeft(c, 4); c += b; \
b -= a; b ^= bitRotateLeft(a, 6); a += c; \
c -= b; c ^= bitRotateLeft(b, 8); b += a; \
a -= c; a ^= bitRotateLeft(c,16); c += b; \
b -= a; b ^= bitRotateLeft(a,19); a += c; \
c -= b; c ^= bitRotateLeft(b, 4); b += a; \
}
// ----------------------------------------------------------------------------
// final -- final mixing of 3 32-bit values (a,b,c) into c
//
......@@ -136,6 +148,18 @@ Description
// 11 8 15 26 3 22 24
// ----------------------------------------------------------------------------
#define bitMixerFinal(a, b, c) \
{ \
c ^= b; c -= bitRotateLeft(b, 14); \
a ^= c; a -= bitRotateLeft(c, 11); \
b ^= a; b -= bitRotateLeft(a, 25); \
c ^= b; c -= bitRotateLeft(b, 16); \
a ^= c; a -= bitRotateLeft(c, 4); \
b ^= a; b -= bitRotateLeft(a, 14); \
c ^= b; c -= bitRotateLeft(b, 24); \
}
// * * * * * * * * * * * * * * Static Functions * * * * * * * * * * * * * * //
// ----------------------------------------------------------------------------
......@@ -165,11 +189,11 @@ Description
//- specialized little-endian code
#if !defined (__BYTE_ORDER) || (__BYTE_ORDER == __LITTLE_ENDIAN)
static uint32_t jenkins_hashlittle
static unsigned jenkins_hashlittle
(
const void *key,
size_t length,
uint32_t initval
unsigned initval
)
{
uint32_t a, b, c;
......@@ -343,11 +367,11 @@ static uint32_t jenkins_hashlittle
// ----------------------------------------------------------------------------
// specialized big-endian code
#if !defined (__BYTE_ORDER) || (__BYTE_ORDER == __BIG_ENDIAN)
static uint32_t jenkins_hashbig
static unsigned jenkins_hashbig
(
const void *key,
size_t length,
uint32_t initval
unsigned initval
)
{
uint32_t a, b, c;
......@@ -447,6 +471,37 @@ static uint32_t jenkins_hashbig
// * * * * * * * * * * * * * * * * Functions * * * * * * * * * * * * * * * * //
unsigned Foam::Hasher
(
const void *key,
size_t length,
unsigned initval
)
{
#ifdef __BYTE_ORDER
# if (__BYTE_ORDER == __BIG_ENDIAN)
return jenkins_hashbig(key, length, initval);
# else
return jenkins_hashlittle(key, length, initval);
# endif
#else
// endian-ness not known at compile-time: runtime endian test
const short endianTest = 0x0100;
// yields 0x01 for big endian
if (*(reinterpret_cast<const char *>(&endianTest)))
{
return jenkins_hashbig(key, length, initval);
}
else
{
return jenkins_hashlittle(key, length, initval);
}
#endif