diff --git a/dtool/src/dtoolbase/Sources.pp b/dtool/src/dtoolbase/Sources.pp index 531295e416..dc757d97af 100644 --- a/dtool/src/dtoolbase/Sources.pp +++ b/dtool/src/dtoolbase/Sources.pp @@ -6,6 +6,7 @@ #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx #define SOURCES \ + addHash.I addHash.h \ atomicAdjust.h \ atomicAdjustDummyImpl.h atomicAdjustDummyImpl.I \ atomicAdjustI386Impl.h atomicAdjustI386Impl.I \ @@ -31,9 +32,10 @@ stl_compares.I stl_compares.h \ pallocator.T pallocator.h \ pdeque.h plist.h pmap.h pset.h pvector.h \ - dlmalloc.c + dlmalloc.c lookup3.h lookup3.c #define INCLUDED_SOURCES \ + addHash.cxx \ atomicAdjustDummyImpl.cxx \ atomicAdjustI386Impl.cxx \ atomicAdjustNsprImpl.cxx \ @@ -48,6 +50,7 @@ mutexSpinlockImpl.cxx #define INSTALL_HEADERS \ + addHash.I addHash.h \ atomicAdjust.h \ atomicAdjustDummyImpl.h atomicAdjustDummyImpl.I \ atomicAdjustI386Impl.h atomicAdjustI386Impl.I \ @@ -71,6 +74,7 @@ selectThreadImpl.h \ stl_compares.I stl_compares.h \ pallocator.T pallocator.h \ - pdeque.h plist.h pmap.h pset.h pvector.h + pdeque.h plist.h pmap.h pset.h pvector.h \ + lookup3.h #end lib_target diff --git a/dtool/src/dtoolbase/addHash.I b/dtool/src/dtoolbase/addHash.I new file mode 100755 index 0000000000..6fd1fddcd4 --- /dev/null +++ b/dtool/src/dtoolbase/addHash.I @@ -0,0 +1,49 @@ +// Filename: add_hash.I +// Created by: drose (01Sep06) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: AddHash::add_hash +// Access: Public, Static +// Description: Adds a linear sequence of uint32 words to the hash. +//////////////////////////////////////////////////////////////////// +INLINE size_t AddHash:: +add_hash(size_t start, const PN_uint32 *words, size_t num_words) { + return (size_t)hashword(words, num_words, (PN_uint32)start); +} + +//////////////////////////////////////////////////////////////////// +// Function: AddHash::add_hash +// Access: Public, Static +// Description: Adds a linear sequence of float32 words to the hash. +//////////////////////////////////////////////////////////////////// +INLINE size_t AddHash:: +add_hash(size_t start, const PN_float32 *floats, size_t num_floats) { + return add_hash(start, (const PN_uint32 *)floats, num_floats); +} + +//////////////////////////////////////////////////////////////////// +// Function: AddHash::add_hash +// Access: Public, Static +// Description: Adds a linear sequence of float64 words to the hash. +//////////////////////////////////////////////////////////////////// +INLINE size_t AddHash:: +add_hash(size_t start, const PN_float64 *floats, size_t num_floats) { + return add_hash(start, (const PN_uint32 *)floats, num_floats * 2); +} + diff --git a/dtool/src/dtoolbase/addHash.cxx b/dtool/src/dtoolbase/addHash.cxx new file mode 100755 index 0000000000..a15e136334 --- /dev/null +++ b/dtool/src/dtoolbase/addHash.cxx @@ -0,0 +1,61 @@ +// Filename: addHash.cxx +// Created by: drose (01Sep06) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "addHash.h" + +//////////////////////////////////////////////////////////////////// +// Function: AddHash::add_hash +// Access: Public, Static +// Description: Adds a linear sequence of bytes to the hash. +//////////////////////////////////////////////////////////////////// +size_t AddHash:: +add_hash(size_t start, const PN_uint8 *bytes, size_t num_bytes) { + size_t num_words = num_bytes >> 2; + size_t remaining_bytes = num_bytes - (num_words << 2); + size_t hash = (size_t)hashword((const PN_uint32 *)bytes, num_words, (PN_uint32)start); + + switch (remaining_bytes) { + case 3: + { + PN_uint32 remaining; + remaining = (bytes[num_bytes - 3] << 16) | (bytes[num_bytes - 2] << 8) | (bytes[num_bytes - 1]); + hash = (size_t)hashword(&remaining, 1, (PN_uint32)hash); + } + break; + + case 2: + { + PN_uint32 remaining; + remaining = (bytes[num_bytes - 2] << 8) | (bytes[num_bytes - 1]); + hash = (size_t)hashword(&remaining, 1, (PN_uint32)hash); + } + break; + + case 1: + { + PN_uint32 remaining; + remaining = (bytes[num_bytes - 1]); + hash = (size_t)hashword(&remaining, 1, (PN_uint32)hash); + } + break; + + default: + break; + } + return hash; +} diff --git a/dtool/src/dtoolbase/addHash.h b/dtool/src/dtoolbase/addHash.h new file mode 100755 index 0000000000..3db0015be8 --- /dev/null +++ b/dtool/src/dtoolbase/addHash.h @@ -0,0 +1,43 @@ +// Filename: addHash.h +// Created by: drose (01Sep06) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef ADDHASH_H +#define ADDHASH_H + +#include "dtoolbase.h" +#include "numeric_types.h" +#include "lookup3.h" + +//////////////////////////////////////////////////////////////////// +// Class : AddHash +// Description : This class is used just as a namespace scope to +// collect together a handful of static functions, which +// are used to wrap calls to Bob Jenkins' public-domain +// hash generation function (defined in lookup3.c). +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL AddHash { +public: + INLINE static size_t add_hash(size_t start, const PN_uint32 *words, size_t num_words); + static size_t add_hash(size_t start, const PN_uint8 *bytes, size_t num_bytes); + INLINE static size_t add_hash(size_t start, const PN_float32 *floats, size_t num_floats); + INLINE static size_t add_hash(size_t start, const PN_float64 *floats, size_t num_floats); +}; + +#include "addHash.I" + +#endif diff --git a/dtool/src/dtoolbase/dtoolbase_composite1.cxx b/dtool/src/dtoolbase/dtoolbase_composite1.cxx index 59c9905009..c49607c22f 100644 --- a/dtool/src/dtoolbase/dtoolbase_composite1.cxx +++ b/dtool/src/dtoolbase/dtoolbase_composite1.cxx @@ -1,3 +1,4 @@ +#include "addHash.cxx" #include "atomicAdjustDummyImpl.cxx" #include "atomicAdjustI386Impl.cxx" #include "atomicAdjustNsprImpl.cxx" diff --git a/dtool/src/dtoolbase/lookup3.c b/dtool/src/dtoolbase/lookup3.c new file mode 100755 index 0000000000..3b5509c764 --- /dev/null +++ b/dtool/src/dtoolbase/lookup3.c @@ -0,0 +1,912 @@ +/* See http://www.burtleburtle.net/bob/hash/ */ + +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ +/*#define SELF_TEST 1*/ + +#include "lookup3.h" + +#include +#include +#include +#include + +#ifdef WORDS_BIGENDIAN +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) ^ ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +-------------------------------------------------------------------- + This works on all machines. To be useful, it requires + -- that the key be an array of PN_uint32's, and + -- that the length be the number of PN_uint32's in the key + + The function hashword() is identical to hashlittle() on little-endian + machines, and identical to hashbig() on big-endian machines, + except that the length has to be measured in PN_uint32s rather than in + bytes. hashlittle() is more complicated than hashword() only because + hashlittle() has to dance around fitting the key bytes into registers. +-------------------------------------------------------------------- +*/ +PN_uint32 hashword( +const PN_uint32 *k, /* the key, an array of PN_uint32 values */ +size_t length, /* the length of the key, in PN_uint32s */ +PN_uint32 initval) /* the previous hash, or an arbitrary value */ +{ + PN_uint32 a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((PN_uint32)length)<<2) + initval; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 PN_uint32's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + return c; +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (PN_uint8 **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const PN_uint8 *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((PN_uint32)k8[10])<<16; /* fall through */ + case 10: c+=((PN_uint32)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((PN_uint32)k8[6])<<16; /* fall through */ + case 6 : b+=((PN_uint32)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((PN_uint32)k8[2])<<16; /* fall through */ + case 2 : a+=((PN_uint32)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const PN_uint16 *k = key; /* read 16-bit chunks */ + const PN_uint8 *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((PN_uint32)k[1])<<16); + b += k[2] + (((PN_uint32)k[3])<<16); + c += k[4] + (((PN_uint32)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const PN_uint8 *)k; + switch(length) + { + case 12: c+=k[4]+(((PN_uint32)k[5])<<16); + b+=k[2]+(((PN_uint32)k[3])<<16); + a+=k[0]+(((PN_uint32)k[1])<<16); + break; + case 11: c+=((PN_uint32)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((PN_uint32)k[3])<<16); + a+=k[0]+(((PN_uint32)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((PN_uint32)k[3])<<16); + a+=k[0]+(((PN_uint32)k[1])<<16); + break; + case 7 : b+=((PN_uint32)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((PN_uint32)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((PN_uint32)k[1])<<16); + break; + case 3 : a+=((PN_uint32)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const PN_uint8 *k = key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((PN_uint32)k[1])<<8; + a += ((PN_uint32)k[2])<<16; + a += ((PN_uint32)k[3])<<24; + b += k[4]; + b += ((PN_uint32)k[5])<<8; + b += ((PN_uint32)k[6])<<16; + b += ((PN_uint32)k[7])<<24; + c += k[8]; + c += ((PN_uint32)k[9])<<8; + c += ((PN_uint32)k[10])<<16; + c += ((PN_uint32)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((PN_uint32)k[11])<<24; + case 11: c+=((PN_uint32)k[10])<<16; + case 10: c+=((PN_uint32)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((PN_uint32)k[7])<<24; + case 7 : b+=((PN_uint32)k[6])<<16; + case 6 : b+=((PN_uint32)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((PN_uint32)k[3])<<24; + case 3 : a+=((PN_uint32)k[2])<<16; + case 2 : a+=((PN_uint32)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +/* + * hashlittle2: return 2 32-bit hash values + * + * This is identical to hashlittle(), except it returns two 32-bit hash + * values instead of just one. This is good enough for hash table + * lookup with 2^^64 buckets, or if you want a second hash if you're not + * happy with the first, or if you want a probably-unique 64-bit ID for + * the key. *pc is better mixed than *pb, so use *pc first. If you want + * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". + */ +void hashlittle2( + const void *key, /* the key to hash */ + size_t length, /* length of the key */ + PN_uint32 *pc, /* IN: primary initval, OUT: primary hash */ + PN_uint32 *pb) /* IN: secondary initval, OUT: secondary hash */ +{ + PN_uint32 a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((PN_uint32)length) + *pc; + c += *pb; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const PN_uint32 *k = key; /* read 32-bit chunks */ +#ifdef VALGRIND + const PN_uint8 *k8; +#endif + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const PN_uint8 *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((PN_uint32)k8[10])<<16; /* fall through */ + case 10: c+=((PN_uint32)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((PN_uint32)k8[6])<<16; /* fall through */ + case 6 : b+=((PN_uint32)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((PN_uint32)k8[2])<<16; /* fall through */ + case 2 : a+=((PN_uint32)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const PN_uint16 *k = key; /* read 16-bit chunks */ + const PN_uint8 *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((PN_uint32)k[1])<<16); + b += k[2] + (((PN_uint32)k[3])<<16); + c += k[4] + (((PN_uint32)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const PN_uint8 *)k; + switch(length) + { + case 12: c+=k[4]+(((PN_uint32)k[5])<<16); + b+=k[2]+(((PN_uint32)k[3])<<16); + a+=k[0]+(((PN_uint32)k[1])<<16); + break; + case 11: c+=((PN_uint32)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((PN_uint32)k[3])<<16); + a+=k[0]+(((PN_uint32)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((PN_uint32)k[3])<<16); + a+=k[0]+(((PN_uint32)k[1])<<16); + break; + case 7 : b+=((PN_uint32)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((PN_uint32)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((PN_uint32)k[1])<<16); + break; + case 3 : a+=((PN_uint32)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const PN_uint8 *k = key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((PN_uint32)k[1])<<8; + a += ((PN_uint32)k[2])<<16; + a += ((PN_uint32)k[3])<<24; + b += k[4]; + b += ((PN_uint32)k[5])<<8; + b += ((PN_uint32)k[6])<<16; + b += ((PN_uint32)k[7])<<24; + c += k[8]; + c += ((PN_uint32)k[9])<<8; + c += ((PN_uint32)k[10])<<16; + c += ((PN_uint32)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((PN_uint32)k[11])<<24; + case 11: c+=((PN_uint32)k[10])<<16; + case 10: c+=((PN_uint32)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((PN_uint32)k[7])<<24; + case 7 : b+=((PN_uint32)k[6])<<16; + case 6 : b+=((PN_uint32)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((PN_uint32)k[3])<<24; + case 3 : a+=((PN_uint32)k[2])<<16; + case 2 : a+=((PN_uint32)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + } + + final(a,b,c); + *pc=c; *pb=b; return; /* zero length strings require no mixing */ +} + + + +/* + * hashbig(): + * This is the same as hashword() on big-endian machines. It is different + * from hashlittle() on all machines. hashbig() takes advantage of + * big-endian byte ordering. + */ +PN_uint32 hashbig( const void *key, size_t length, PN_uint32 initval) +{ + PN_uint32 a,b,c; + union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((PN_uint32)length) + initval; + + u.ptr = key; + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { + const PN_uint32 *k = key; /* read 32-bit chunks */ +#ifdef VALGRIND + const PN_uint8 *k8; +#endif + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]<<8" actually reads beyond the end of the string, but + * then shifts out the part it's not allowed to read. Because the + * string is aligned, the illegal read is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]<<8; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]<<16; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]<<24; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]<<8; a+=k[0]; break; + case 6 : b+=k[1]<<16; a+=k[0]; break; + case 5 : b+=k[1]<<24; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]<<8; break; + case 2 : a+=k[0]<<16; break; + case 1 : a+=k[0]<<24; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const PN_uint8 *)k; + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((PN_uint32)k8[10])<<8; /* fall through */ + case 10: c+=((PN_uint32)k8[9])<<16; /* fall through */ + case 9 : c+=((PN_uint32)k8[8])<<24; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((PN_uint32)k8[6])<<8; /* fall through */ + case 6 : b+=((PN_uint32)k8[5])<<16; /* fall through */ + case 5 : b+=((PN_uint32)k8[4])<<24; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((PN_uint32)k8[2])<<8; /* fall through */ + case 2 : a+=((PN_uint32)k8[1])<<16; /* fall through */ + case 1 : a+=((PN_uint32)k8[0])<<24; break; + case 0 : return c; + } + +#endif /* !VALGRIND */ + + } else { /* need to read the key one byte at a time */ + const PN_uint8 *k = key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += ((PN_uint32)k[0])<<24; + a += ((PN_uint32)k[1])<<16; + a += ((PN_uint32)k[2])<<8; + a += ((PN_uint32)k[3]); + b += ((PN_uint32)k[4])<<24; + b += ((PN_uint32)k[5])<<16; + b += ((PN_uint32)k[6])<<8; + b += ((PN_uint32)k[7]); + c += ((PN_uint32)k[8])<<24; + c += ((PN_uint32)k[9])<<16; + c += ((PN_uint32)k[10])<<8; + c += ((PN_uint32)k[11]); + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[11]; + case 11: c+=((PN_uint32)k[10])<<8; + case 10: c+=((PN_uint32)k[9])<<16; + case 9 : c+=((PN_uint32)k[8])<<24; + case 8 : b+=k[7]; + case 7 : b+=((PN_uint32)k[6])<<8; + case 6 : b+=((PN_uint32)k[5])<<16; + case 5 : b+=((PN_uint32)k[4])<<24; + case 4 : a+=k[3]; + case 3 : a+=((PN_uint32)k[2])<<8; + case 2 : a+=((PN_uint32)k[1])<<16; + case 1 : a+=((PN_uint32)k[0])<<24; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +#ifdef SELF_TEST + +/* used for timings */ +void driver1() +{ + PN_uint8 buf[256]; + PN_uint32 i; + PN_uint32 h=0; + time_t a,z; + + time(&a); + for (i=0; i<256; ++i) buf[i] = 'x'; + for (i=0; i<1; ++i) + { + h = hashlittle(&buf[0],1,h); + } + time(&z); + if (z-a > 0) printf("time %d %.8x\n", z-a, h); +} + +/* check that every input bit changes every output bit half the time */ +#define HASHSTATE 1 +#define HASHLEN 1 +#define MAXPAIR 60 +#define MAXLEN 70 +void driver2() +{ + PN_uint8 qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1]; + PN_uint32 c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z; + PN_uint32 e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE]; + PN_uint32 x[HASHSTATE],y[HASHSTATE]; + PN_uint32 hlen; + + printf("No more than %d trials should ever be needed \n",MAXPAIR/2); + for (hlen=0; hlen < MAXLEN; ++hlen) + { + z=0; + for (i=0; i>(8-j)); + c[0] = hashlittle(a, hlen, m); + b[i] ^= ((k+1)<>(8-j)); + d[0] = hashlittle(b, hlen, m); + /* check every bit is 1, 0, set, and not set at least once */ + for (l=0; lz) z=k; + if (k==MAXPAIR) + { + printf("Some bit didn't change: "); + printf("%.8x %.8x %.8x %.8x %.8x %.8x ", + e[0],f[0],g[0],h[0],x[0],y[0]); + printf("i %d j %d m %d len %d\n", i, j, m, hlen); + } + if (z==MAXPAIR) goto done; + } + } + } + done: + if (z < MAXPAIR) + { + printf("Mix success %2d bytes %2d initvals ",i,m); + printf("required %d trials\n", z/2); + } + } + printf("\n"); +} + +/* Check for reading beyond the end of the buffer and alignment problems */ +void driver3() +{ + PN_uint8 buf[MAXLEN+20], *b; + PN_uint32 len; + PN_uint8 q[] = "This is the time for all good men to come to the aid of their country..."; + PN_uint32 h; + PN_uint8 qq[] = "xThis is the time for all good men to come to the aid of their country..."; + PN_uint32 i; + PN_uint8 qqq[] = "xxThis is the time for all good men to come to the aid of their country..."; + PN_uint32 j; + PN_uint8 qqqq[] = "xxxThis is the time for all good men to come to the aid of their country..."; + PN_uint32 ref,x,y; + PN_uint8 *p; + + printf("Endianness. These lines should all be the same (for values filled in):\n"); + printf("%.8x %.8x %.8x\n", + hashword((const PN_uint32 *)q, (sizeof(q)-1)/4, 13), + hashword((const PN_uint32 *)q, (sizeof(q)-5)/4, 13), + hashword((const PN_uint32 *)q, (sizeof(q)-9)/4, 13)); + p = q; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qq[1]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qqq[2]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qqqq[3]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + printf("\n"); + for (h=0, b=buf+1; h<8; ++h, ++b) + { + for (i=0; i > > -class phash_map : public hash_map > > { +class phash_map : public stdext::hash_map > > { public: - phash_map() : hash_map > >() { } - phash_map(const phash_map ©) : hash_map > >(copy) { } - phash_map(const Compare &comp) : hash_map > >(comp) { } + phash_map() : stdext::hash_map > >() { } + phash_map(const phash_map ©) : stdext::hash_map > >(copy) { } + phash_map(const Compare &comp) : stdext::hash_map > >(comp) { } }; //////////////////////////////////////////////////////////////////// @@ -152,11 +152,11 @@ public: // memory. //////////////////////////////////////////////////////////////////// template > > -class phash_multimap : public hash_multimap > > { +class phash_multimap : public stdext::hash_multimap > > { public: - phash_multimap() : hash_multimap > >() { } - phash_multimap(const phash_multimap ©) : hash_multimap > >(copy) { } - phash_multimap(const Compare &comp) : hash_multimap > >(comp) { } + phash_multimap() : stdext::hash_multimap > >() { } + phash_multimap(const phash_multimap ©) : stdext::hash_multimap > >(copy) { } + phash_multimap(const Compare &comp) : stdext::hash_multimap > >(comp) { } }; #else // HAVE_STL_HASH diff --git a/dtool/src/dtoolbase/pset.h b/dtool/src/dtoolbase/pset.h index 9af959b4c2..e01a98a6ee 100644 --- a/dtool/src/dtoolbase/pset.h +++ b/dtool/src/dtoolbase/pset.h @@ -35,8 +35,8 @@ #define pmultiset multiset #ifdef HAVE_STL_HASH -#define phash_set hash_set -#define phash_multiset hash_multiset +#define phash_set stdext::hash_set +#define phash_multiset stdext::hash_multiset #else // HAVE_STL_HASH #define phash_set set #define phash_multiset multiset @@ -129,11 +129,11 @@ public: // memory. //////////////////////////////////////////////////////////////////// template > > -class phash_set : public hash_set > { +class phash_set : public stdext::hash_set > { public: - phash_set() : hash_set >() { } - phash_set(const phash_set ©) : hash_set >(copy) { } - phash_set(const Compare &comp) : hash_set >(comp) { } + phash_set() : stdext::hash_set >() { } + phash_set(const phash_set ©) : stdext::hash_set >(copy) { } + phash_set(const Compare &comp) : stdext::hash_set >(comp) { } }; //////////////////////////////////////////////////////////////////// @@ -144,11 +144,11 @@ public: // memory. //////////////////////////////////////////////////////////////////// template > > -class phash_multiset : public hash_multiset > { +class phash_multiset : public stdext::hash_multiset > { public: - phash_multiset() : hash_multiset >() { } - phash_multiset(const phash_multiset ©) : hash_multiset >(copy) { } - phash_multiset(const Compare &comp) : hash_multiset >(comp) { } + phash_multiset() : stdext::hash_multiset >() { } + phash_multiset(const phash_multiset ©) : stdext::hash_multiset >(copy) { } + phash_multiset(const Compare &comp) : stdext::hash_multiset >(comp) { } }; #else // HAVE_STL_HASH diff --git a/dtool/src/dtoolbase/stl_compares.I b/dtool/src/dtoolbase/stl_compares.I index ec5d0c79b6..da0708bb71 100644 --- a/dtool/src/dtoolbase/stl_compares.I +++ b/dtool/src/dtoolbase/stl_compares.I @@ -91,7 +91,8 @@ operator () (const Key &a, const Key &b) const { template INLINE size_t integer_hash:: add_hash(size_t hash, const Key &key) { - return (hash * 31) + (size_t)key; + PN_uint32 key32 = (PN_uint32)key; + return AddHash::add_hash(hash, &key32, 1); } //////////////////////////////////////////////////////////////////// @@ -136,7 +137,8 @@ operator () (const Key &a, const Key &b) const { template INLINE size_t floating_point_hash:: add_hash(size_t hash, const Key &key) const { - return (hash * 31) + (size_t)(cfloor(key / _threshold + 0.5f)); + PN_uint32 key32 = (PN_uint32)(key / _threshold + 0.5f); + return AddHash::add_hash(hash, &key32, 1); } //////////////////////////////////////////////////////////////////// @@ -160,11 +162,12 @@ operator () (const Key &key) const { template INLINE size_t sequence_hash:: add_hash(size_t hash, const Key &key) { - TYPENAME Key::const_iterator ki; - for (ki = key.begin(); ki != key.end(); ++ki) { - hash = (hash * 31) + (size_t)(*ki); - } - return hash; +#ifdef _DEBUG + // We assume that the sequence is laid out sequentially in memory. + assert(&(*key.end()) - &key[0] == key.size()); +#endif + size_t num_bytes = (key.size() * sizeof(key[0])); + return AddHash::add_hash(hash, (const PN_uint8 *)&key[0], num_bytes); } //////////////////////////////////////////////////////////////////// diff --git a/dtool/src/dtoolbase/stl_compares.h b/dtool/src/dtoolbase/stl_compares.h index 882633acc8..50ac4d9945 100644 --- a/dtool/src/dtoolbase/stl_compares.h +++ b/dtool/src/dtoolbase/stl_compares.h @@ -22,11 +22,14 @@ #include "dtoolbase.h" #include "cmath.h" #include "nearly_zero.h" +#include "addHash.h" + +#include #ifdef HAVE_STL_HASH #include // for hash_compare -#define stl_hash_compare hash_compare +#define stl_hash_compare stdext::hash_compare #else diff --git a/dtool/src/parser-inc/hash_map b/dtool/src/parser-inc/hash_map index b8db9ba528..d7fce7e94e 100644 --- a/dtool/src/parser-inc/hash_map +++ b/dtool/src/parser-inc/hash_map @@ -28,30 +28,32 @@ #include #include -template > > -class hash_map { -public: - typedef key key_type; - typedef element data_type; - typedef element mapped_type; - typedef pair value_type; - typedef compare key_compare; +namespace stdext { + template > > + class hash_map { + public: + typedef key key_type; + typedef element data_type; + typedef element mapped_type; + typedef pair value_type; + typedef compare key_compare; - typedef element *pointer; - typedef const element *const_pointer; - typedef element &reference; - typedef const element &const_reference; + typedef element *pointer; + typedef const element *const_pointer; + typedef element &reference; + typedef const element &const_reference; - class iterator; - class const_iterator; - class reverse_iterator; - class const_reverse_iterator; - typedef size_t size_type; - class difference_type; -}; + class iterator; + class const_iterator; + class reverse_iterator; + class const_reverse_iterator; + typedef size_t size_type; + class difference_type; + }; -template > > -class hash_multimap : public hash_map { + template > > + class hash_multimap : public hash_map { + }; }; #endif diff --git a/dtool/src/parser-inc/hash_set b/dtool/src/parser-inc/hash_set index f6a428f123..876ad90374 100644 --- a/dtool/src/parser-inc/hash_set +++ b/dtool/src/parser-inc/hash_set @@ -27,29 +27,33 @@ #include #include -template > > -class hash_set { -public: - typedef key key_type; - typedef key value_type; - typedef compare key_compare; - typedef compare value_compare; +namespace stdext { - typedef key *pointer; - typedef const key *const_pointer; - typedef key &reference; - typedef const key &const_reference; + template > > + class hash_set { + public: + typedef key key_type; + typedef key value_type; + typedef compare key_compare; + typedef compare value_compare; - class iterator; - class const_iterator; - class reverse_iterator; - class const_reverse_iterator; - typedef size_t size_type; - class difference_type; -}; + typedef key *pointer; + typedef const key *const_pointer; + typedef key &reference; + typedef const key &const_reference; + + class iterator; + class const_iterator; + class reverse_iterator; + class const_reverse_iterator; + typedef size_t size_type; + class difference_type; + }; + + template > > + class hash_multiset : public hash_set { + }; -template > > -class hash_multiset : public hash_set { }; #endif diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index 30b9515ee4..43708928c6 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -138,6 +138,26 @@ operator < (const RenderState &other) const { CompareTo()); } +//////////////////////////////////////////////////////////////////// +// Function: RenderState::get_hash +// Access: Published +// Description: Returns a suitable hash value for phash_map. +//////////////////////////////////////////////////////////////////// +size_t RenderState:: +get_hash() const { + size_t hash = 0; + + // hash = sequence_hash::add_hash(hash, _attributes); + Attributes::const_iterator ai; + for (ai = _attributes.begin(); ai != _attributes.end(); ++ai) { + const Attribute &attrib = *ai; + hash = pointer_hash::add_hash(hash, attrib._attrib); + hash = int_hash::add_hash(hash, attrib._override); + } + + return hash; +} + //////////////////////////////////////////////////////////////////// // Function: RenderState::cull_callback // Access: Published diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index 0fdffeda2b..12858f12d9 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -74,6 +74,7 @@ public: PUBLISHED: bool operator < (const RenderState &other) const; + size_t get_hash() const; INLINE bool is_empty() const; INLINE int get_num_attribs() const; @@ -211,7 +212,7 @@ private: // to the cache, which is encoded in _composition_cache and // _invert_composition_cache. static ReMutex *_states_lock; - typedef pset > States; + typedef phash_set > States; static States *_states; static CPT(RenderState) _empty_state; diff --git a/panda/src/pgraph/transformState.I b/panda/src/pgraph/transformState.I index a75e5918c6..9799c6cc42 100644 --- a/panda/src/pgraph/transformState.I +++ b/panda/src/pgraph/transformState.I @@ -33,6 +33,17 @@ operator < (const TransformState &other) const { return sorts_less(other, uniquify_matrix); } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::get_hash +// Access: Published +// Description: Returns a suitable hash value for phash_map. +//////////////////////////////////////////////////////////////////// +INLINE size_t TransformState:: +get_hash() const { + check_hash(); + return _hash; +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::make_pos // Access: Published, Static @@ -751,6 +762,20 @@ flush_level() { _cache_counter.flush_level(); } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::check_hash +// Access: Private +// Description: Ensures that we know the hash value. +//////////////////////////////////////////////////////////////////// +INLINE void TransformState:: +check_hash() const { + // This pretends to be a const function, even though it's not, + // because it only updates a transparent cache value. + if ((_flags & F_hash_known) == 0) { + ((TransformState *)this)->calc_hash(); + } +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::check_singular // Access: Private @@ -824,6 +849,17 @@ check_mat() const { } } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::calc_hash +// Access: Private +// Description: Computes the hash value. +//////////////////////////////////////////////////////////////////// +INLINE void TransformState:: +calc_hash() { + MutexHolder holder(_lock); + do_calc_hash(); +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::calc_components // Access: Private diff --git a/panda/src/pgraph/transformState.cxx b/panda/src/pgraph/transformState.cxx index 8b8b6a94b3..d720923601 100644 --- a/panda/src/pgraph/transformState.cxx +++ b/panda/src/pgraph/transformState.cxx @@ -181,52 +181,6 @@ sorts_less(const TransformState &other, bool uniquify_matrix) const { } } -//////////////////////////////////////////////////////////////////// -// Function: TransformState::get_hash() -// Access: Published -// Description: Computes a suitable hash value for phash_map. -//////////////////////////////////////////////////////////////////// -size_t TransformState:: -get_hash() const { - size_t hash = 0; - - static const int significant_flags = - (F_is_invalid | F_is_identity | F_components_given | F_hpr_given | F_is_2d); - - int flags = (_flags & significant_flags); - hash = int_hash::add_hash(hash, flags); - - if ((_flags & (F_is_invalid | F_is_identity)) == 0) { - // Only bother to put the rest of the stuff in the hash if the - // transform is not invalid or empty. - - if ((_flags & (F_components_given | F_hpr_given | F_quat_given)) == - (F_components_given | F_hpr_given | F_quat_given)) { - // If the transform was specified componentwise, hash it - // componentwise. - hash = _pos.add_hash(hash); - if ((_flags & F_hpr_given) != 0) { - hash = _hpr.add_hash(hash); - - } else if ((_flags & F_quat_given) != 0) { - hash = _quat.add_hash(hash); - } - - hash = _scale.add_hash(hash); - hash = _shear.add_hash(hash); - - } else { - // Otherwise, hash the pointer only--any two different - // matrix-based TransformStates are considered to be different, - // even if their matrices have the same values. - - hash = pointer_hash::add_hash(hash, this); - } - } - - return hash; -} - //////////////////////////////////////////////////////////////////// // Function: TransformState::make_identity // Access: Published, Static @@ -1736,6 +1690,52 @@ remove_cache_pointers() { } } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::do_calc_hash +// Access: Private +// Description: Computes a suitable hash value for phash_map. +//////////////////////////////////////////////////////////////////// +void TransformState:: +do_calc_hash() { + _hash = 0; + + static const int significant_flags = + (F_is_invalid | F_is_identity | F_components_given | F_hpr_given | F_is_2d); + + int flags = (_flags & significant_flags); + _hash = int_hash::add_hash(_hash, flags); + + if ((_flags & (F_is_invalid | F_is_identity)) == 0) { + // Only bother to put the rest of the stuff in the hash if the + // transform is not invalid or empty. + + if ((_flags & (F_components_given | F_hpr_given | F_quat_given)) == + (F_components_given | F_hpr_given | F_quat_given)) { + // If the transform was specified componentwise, hash it + // componentwise. + _hash = _pos.add_hash(_hash); + if ((_flags & F_hpr_given) != 0) { + _hash = _hpr.add_hash(_hash); + + } else if ((_flags & F_quat_given) != 0) { + _hash = _quat.add_hash(_hash); + } + + _hash = _scale.add_hash(_hash); + _hash = _shear.add_hash(_hash); + + } else { + // Otherwise, hash the pointer only--any two different + // matrix-based TransformStates are considered to be different, + // even if their matrices have the same values. + + _hash = pointer_hash::add_hash(_hash, this); + } + } + + _flags |= F_hash_known; +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::calc_singular // Access: Private diff --git a/panda/src/pgraph/transformState.h b/panda/src/pgraph/transformState.h index 1f27475510..d96d89d392 100644 --- a/panda/src/pgraph/transformState.h +++ b/panda/src/pgraph/transformState.h @@ -74,7 +74,7 @@ public: PUBLISHED: INLINE bool operator < (const TransformState &other) const; bool sorts_less(const TransformState &other, bool uniquify_matrix) const; - size_t get_hash() const; + INLINE size_t get_hash() const; static CPT(TransformState) make_identity(); static CPT(TransformState) make_invalid(); @@ -266,11 +266,14 @@ private: private: // This is the actual data within the TransformState. + INLINE void check_hash() const; INLINE void check_singular() const; INLINE void check_components() const; INLINE void check_hpr() const; INLINE void check_quat() const; INLINE void check_mat() const; + INLINE void calc_hash(); + void do_calc_hash(); void calc_singular(); INLINE void calc_components(); void do_calc_components(); @@ -307,12 +310,14 @@ private: F_has_nonzero_shear = 0x00004000, F_is_destructing = 0x00008000, F_is_2d = 0x00010000, + F_hash_known = 0x00020000, }; LPoint3f _pos; LVecBase3f _hpr, _scale, _shear; LQuaternionf _quat; LMatrix4f _mat; LMatrix4f *_inv_mat; + size_t _hash; unsigned int _flags;