Skip to content

Commit 4da3c4d

Browse files
committed
Optimized XMPPJID's hashing function using murmur hashing algorithm.
1 parent 7b301b2 commit 4da3c4d

File tree

1 file changed

+147
-1
lines changed

1 file changed

+147
-1
lines changed

Core/XMPPJID.m

Lines changed: 147 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,153 @@ - (XMPPJID *)jidWithNewResource:(NSString *)newResource
379379

380380
- (NSUInteger)hash
381381
{
382-
return [[self full] hash];
382+
// We used to do this:
383+
// return [[self full] hash];
384+
//
385+
// It was functional but less than optimal because it required the creation of a new NSString everytime.
386+
// Now the hashing of a string itself is extremely fast,
387+
// so combining 3 hashes is much faster than creating a new string.
388+
// To accomplish this we use the murmur hashing algorithm.
389+
//
390+
// MurmurHash2 was written by Austin Appleby, and is placed in the public domain.
391+
// http://code.google.com/p/smhasher
392+
393+
NSUInteger uhash = [user hash];
394+
NSUInteger dhash = [domain hash];
395+
NSUInteger rhash = [resource hash];
396+
397+
if (NSUIntegerMax == UINT32_MAX) // Should be optimized out via compiler since these are constants
398+
{
399+
// MurmurHash2 (32-bit)
400+
//
401+
// uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed )
402+
//
403+
// Normally one would pass a chunk of data ('key') and associated data chunk length ('len').
404+
// Instead we're going to use our 3 hashes.
405+
// And we're going to randomly make up a 'seed'.
406+
407+
const uint32_t seed = 0xa2f1b6f; // Some random value I made up
408+
const uint32_t len = 12; // 3 hashes, each 4 bytes = 12 bytes
409+
410+
// 'm' and 'r' are mixing constants generated offline.
411+
// They're not really 'magic', they just happen to work well.
412+
413+
const uint32_t m = 0x5bd1e995;
414+
const int r = 24;
415+
416+
// Initialize the hash to a 'random' value
417+
418+
uint32_t h = seed ^ len;
419+
uint32_t k;
420+
421+
// Mix uhash
422+
423+
k = uhash;
424+
425+
k *= m;
426+
k ^= k >> r;
427+
k *= m;
428+
429+
h *= m;
430+
h ^= k;
431+
432+
// Mix dhash
433+
434+
k = dhash;
435+
436+
k *= m;
437+
k ^= k >> r;
438+
k *= m;
439+
440+
h *= m;
441+
h ^= k;
442+
443+
// Mix rhash
444+
445+
k = rhash;
446+
447+
k *= m;
448+
k ^= k >> r;
449+
k *= m;
450+
451+
h *= m;
452+
h ^= k;
453+
454+
// Do a few final mixes of the hash to ensure the last few
455+
// bytes are well-incorporated.
456+
457+
h ^= h >> 13;
458+
h *= m;
459+
h ^= h >> 15;
460+
461+
return (NSUInteger)h;
462+
}
463+
else
464+
{
465+
// MurmurHash2 (64-bit)
466+
//
467+
// uint64_t MurmurHash64A ( const void * key, int len, uint64_t seed )
468+
//
469+
// Normally one would pass a chunk of data ('key') and associated data chunk length ('len').
470+
// Instead we're going to use our 3 hashes.
471+
// And we're going to randomly make up a 'seed'.
472+
473+
const uint32_t seed = 0xa2f1b6f; // Some random value I made up
474+
const uint32_t len = 24; // 3 hashes, each 8 bytes = 24 bytes
475+
476+
// 'm' and 'r' are mixing constants generated offline.
477+
// They're not really 'magic', they just happen to work well.
478+
479+
const uint64_t m = 0xc6a4a7935bd1e995LLU;
480+
const int r = 47;
481+
482+
// Initialize the hash to a 'random' value
483+
484+
uint64_t h = seed ^ (len * m);
485+
uint64_t k;
486+
487+
// Mix uhash
488+
489+
k = uhash;
490+
491+
k *= m;
492+
k ^= k >> r;
493+
k *= m;
494+
495+
h ^= k;
496+
h *= m;
497+
498+
// Mix dhash
499+
500+
k = dhash;
501+
502+
k *= m;
503+
k ^= k >> r;
504+
k *= m;
505+
506+
h ^= k;
507+
h *= m;
508+
509+
// Mix rhash
510+
511+
k = rhash;
512+
513+
k *= m;
514+
k ^= k >> r;
515+
k *= m;
516+
517+
h ^= k;
518+
h *= m;
519+
520+
// Do a few final mixes of the hash to ensure the last few
521+
// bytes are well-incorporated.
522+
523+
h ^= h >> r;
524+
h *= m;
525+
h ^= h >> r;
526+
527+
return (NSUInteger)h;
528+
}
383529
}
384530

385531
- (BOOL)isEqual:(id)anObject

0 commit comments

Comments
 (0)