BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslh_wyhashincrementalalgorithm.h
Go to the documentation of this file.
1/// @file bslh_wyhashincrementalalgorithm.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslh_wyhashincrementalalgorithm.h -*-C++-*-
8#ifndef INCLUDED_BSLH_WYHASHINCREMENTALALGORITHM
9#define INCLUDED_BSLH_WYHASHINCREMENTALALGORITHM
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bslh_wyhashincrementalalgorithm bslh_wyhashincrementalalgorithm
15/// @brief Provide an implementation of the WyHash algorithm final v3.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bslh
19/// @{
20/// @addtogroup bslh_wyhashincrementalalgorithm
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bslh_wyhashincrementalalgorithm-purpose"> Purpose</a>
25/// * <a href="#bslh_wyhashincrementalalgorithm-classes"> Classes </a>
26/// * <a href="#bslh_wyhashincrementalalgorithm-description"> Description </a>
27/// * <a href="#bslh_wyhashincrementalalgorithm-security"> Security </a>
28/// * <a href="#bslh_wyhashincrementalalgorithm-denial-of-service-protection"> Denial of Service (DoS) Protection </a>
29/// * <a href="#bslh_wyhashincrementalalgorithm-hash-distribution"> Hash Distribution </a>
30/// * <a href="#bslh_wyhashincrementalalgorithm-alignment-independence"> Alignment-Independence </a>
31/// * <a href="#bslh_wyhashincrementalalgorithm-subdivision-invariance"> Subdivision-Invariance </a>
32/// * <a href="#bslh_wyhashincrementalalgorithm-speed"> Speed </a>
33/// * <a href="#bslh_wyhashincrementalalgorithm-usage"> Usage </a>
34/// * <a href="#bslh_wyhashincrementalalgorithm-example-creating-and-using-a-hash-table"> Example: Creating and Using a Hash Table </a>
35///
36/// # Purpose {#bslh_wyhashincrementalalgorithm-purpose}
37/// Provide an implementation of the WyHash algorithm final v3.
38///
39/// # Classes {#bslh_wyhashincrementalalgorithm-classes}
40///
41/// - bslh::WyHashIncrementalAlgorithm: functor implementing the WyHash algorithm
42///
43/// @see bslh_hash
44///
45/// # Description {#bslh_wyhashincrementalalgorithm-description}
46/// `bslh::WyHashIncrementalAlgorithm` implements the WyHash
47/// algorithm by Wang Yi et al (see implementation file for full list of
48/// authors) with modifications. This algorithm is known to be very fast yet
49/// have good avalanche behavior.
50///
51/// The original algorithm was downloaded from
52/// https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h which had been
53/// updated on September 14, 2021, last commit 166f352, and modified to conform
54/// to BDE coding conventions with no change in the binary results produced.
55///
56/// The modifications are:
57/// * A property is added that hashing a segment in one pass will yeild the
58/// same result as hashing it in pieces.
59/// * Byte-swapping is eliminated for speed, and therefore the algorithm yields
60/// different results depending on the byte-order of the host.
61///
62/// ## Security {#bslh_wyhashincrementalalgorithm-security}
63///
64///
65/// WyHash is *not* a "Cryptographically Secure" hash. It is "Cryptographically
66/// Strong", but not "Cryptographically Secure". In order to be
67/// cryptographically secure, an algorithm must, among other things, provide
68/// "Collision Resistance", described in
69/// https://en.wikipedia.org/wiki/Collision_resistance , meaning that it should
70/// be difficult to find two different messages `m1` and `m2` such that
71/// `hash(m1) == hash(m2)`. Because of the limited sized output (only 2**64
72/// possibilities) and the fast execution time of the algorithm, it is probable
73/// to find two such values searching only about `sqrt(2**64) == 2**32` inputs,
74/// which wont take long.
75///
76/// WyHash *is*, however, a cryptographically strong PRF (pseudo-random
77/// function). This means, assuming a cryptographically secure random seed is
78/// given, the output of this algorithm will be indistinguishable from a uniform
79/// random distribution. This property is enough for the algorithm to be able
80/// to protect a hash table from malicious Denial of Service (DoS) attacks.
81///
82/// ### Denial of Service (DoS) Protection {#bslh_wyhashincrementalalgorithm-denial-of-service-protection}
83///
84///
85/// Given a cryptographically secure seed, this algorithm will produce hashes
86/// with a distribution that is indistinguishable from random. This
87/// distribution means that there is no way for an attacker to predict which
88/// keys will cause collisions, meaning that this algorithm can help mitigate
89/// Denial of Service (DoS) attacks on a hash table. DoS attacks occur when an
90/// attacker deliberately degrades the performance of the hash table by
91/// inserting data that will collide to the same bucket, causing an average
92/// constant time lookup to become a linear search. This protection is only
93/// effective if the seed provided is a cryptographically secure random number
94/// that is not available to the attacker.
95///
96/// ## Hash Distribution {#bslh_wyhashincrementalalgorithm-hash-distribution}
97///
98///
99/// Output hashes will be well distributed and will avalanche, which means
100/// changing one bit of the input will change approximately 50% of the output
101/// bits. This will prevent similar values from funneling to the same hash or
102/// bucket.
103///
104/// ## Alignment-Independence {#bslh_wyhashincrementalalgorithm-alignment-independence}
105///
106///
107/// The value obtained by hashing a segment of memory is independent of the
108/// alignment of the segment of memory.
109///
110/// ## Subdivision-Invariance {#bslh_wyhashincrementalalgorithm-subdivision-invariance}
111///
112///
113/// Note that this algorithm is *subdivision-invariant* (see
114/// {@ref bslh_hash |Subdivision-Invariance}).
115///
116/// ## Speed {#bslh_wyhashincrementalalgorithm-speed}
117///
118///
119/// This algorithm is at least 2X faster than Spooky on all sizes of objects on
120/// Linux, Windows, and Solaris. On Aix it is about twice as fast as Spooky on
121/// small objects and about 50% slower on large objects.
122///
123/// ## Usage {#bslh_wyhashincrementalalgorithm-usage}
124///
125///
126/// This section illustrates intended usage of this component.
127///
128/// ### Example: Creating and Using a Hash Table {#bslh_wyhashincrementalalgorithm-example-creating-and-using-a-hash-table}
129///
130///
131/// Suppose we have any array of types that define `operator==`, and we want a
132/// fast way to find out if values are contained in the array. We can create a
133/// `HashTable` data structure that is capable of looking up values in O(1)
134/// time.
135///
136/// Further suppose that we will be storing futures (the financial instruments)
137/// in this table. Since futures have standardized names, we don't have to
138/// worry about any malicious values causing collisions. We will want to use a
139/// general purpose hashing algorithm with a good hash distribution and good
140/// speed. This algorithm will need to be in the form of a hash functor -- an
141/// object that will take objects stored in our array as input, and yield a
142/// 64-bit int value. The functor can pass the attributes of the `TYPE` that
143/// are salient to hashing into the hashing algorithm, and then return the hash
144/// that is produced.
145///
146/// We can use the result of the hash function to index into our array of
147/// `buckets`. Each `bucket` is simply a pointer to a value in our original
148/// array of `TYPE` objects.
149///
150/// First, we define our `HashTable` template class, with the two type
151/// parameters: `TYPE` (the type being referenced) and `HASHER` (a functor that
152/// produces the hash).
153/// @code
154/// template <class TYPE, class HASHER>
155/// class HashTable {
156/// @endcode
157/// This `class template` implements a hash table providing fast lookup of an
158/// external, non-owned, array of values of (template parameter) `TYPE`.
159///
160/// The (template parameter) `TYPE` shall have a transitive, symmetric
161/// `operator==` function. There is no requirement that it have any kind of
162/// creator defined.
163///
164/// The `HASHER` template parameter type must be a functor with a method having
165/// the following signature:
166/// @code
167/// size_t operator()(TYPE) const;
168/// -OR-
169/// size_t operator()(const TYPE&) const;
170/// @endcode
171/// and `HASHER` shall have a publicly accessible default constructor and
172/// destructor.
173///
174/// Note that this hash table has numerous simplifications because we know the
175/// size of the array and never have to resize the table.
176/// @code
177/// // DATA
178/// const TYPE *d_values; // Array of values table is to
179/// // hold
180/// size_t d_numValues; // Length of 'd_values'.
181/// const TYPE **d_bucketArray; // Contains ptrs into 'd_values'
182/// size_t d_bucketArrayMask; // Will always be '2^N - 1'.
183/// HASHER d_hasher; // User supplied hashing algorithm
184///
185/// private:
186/// // PRIVATE ACCESSORS
187/// bool lookup(size_t *idx,
188/// const TYPE& value,
189/// size_t hashValue) const;
190/// // Look up the specified 'value', having the specified 'hashValue',
191/// // and load its index in 'd_bucketArray' into the specified 'idx'.
192/// // If not found, return the vacant entry in 'd_bucketArray' where
193/// // it should be inserted. Return 'true' if 'value' is found and
194/// // 'false' otherwise.
195///
196/// public:
197/// // CREATORS
198/// HashTable(const TYPE *valuesArray,
199/// size_t numValues);
200/// // Create a hash table referring to the specified 'valuesArray'
201/// // having length of the specified 'numValues'. No value in
202/// // 'valuesArray' shall have the same value as any of the other
203/// // values in 'valuesArray'
204///
205/// ~HashTable();
206/// // Free up memory used by this hash table.
207///
208/// // ACCESSORS
209/// bool contains(const TYPE& value) const;
210/// // Return true if the specified 'value' is found in the table and
211/// // false otherwise.
212/// };
213///
214/// // PRIVATE ACCESSORS
215/// template <class TYPE, class HASHER>
216/// bool HashTable<TYPE, HASHER>::lookup(size_t *idx,
217/// const TYPE& value,
218/// size_t hashValue) const
219/// {
220/// const TYPE *ptr;
221/// for (*idx = hashValue & d_bucketArrayMask; (ptr = d_bucketArray[*idx]);
222/// *idx = (*idx + 1) & d_bucketArrayMask) {
223/// if (value == *ptr) {
224/// return true; // RETURN
225/// }
226/// }
227///
228/// // value was not found in table
229///
230/// return false;
231/// }
232///
233/// // CREATORS
234/// template <class TYPE, class HASHER>
235/// HashTable<TYPE, HASHER>::HashTable(const TYPE *valuesArray,
236/// size_t numValues)
237/// : d_values(valuesArray)
238/// , d_numValues(numValues)
239/// , d_hasher()
240/// {
241/// size_t bucketArrayLength = 4;
242/// while (bucketArrayLength < numValues * 4) {
243/// bucketArrayLength *= 2;
244///
245/// }
246/// d_bucketArrayMask = bucketArrayLength - 1;
247/// d_bucketArray = new const TYPE *[bucketArrayLength];
248/// memset(d_bucketArray, 0, bucketArrayLength * sizeof(TYPE *));
249///
250/// for (unsigned i = 0; i < numValues; ++i) {
251/// const TYPE& value = d_values[i];
252/// size_t idx;
253/// const bool found = lookup(&idx, value, d_hasher(value));
254/// BSLS_ASSERT_OPT(!found); (void) found;
255/// d_bucketArray[idx] = &d_values[i];
256/// }
257/// }
258///
259/// template <class TYPE, class HASHER>
260/// HashTable<TYPE, HASHER>::~HashTable()
261/// {
262/// delete [] d_bucketArray;
263/// }
264///
265/// // ACCESSORS
266/// template <class TYPE, class HASHER>
267/// bool HashTable<TYPE, HASHER>::contains(const TYPE& value) const
268/// {
269/// size_t idx;
270/// return lookup(&idx, value, d_hasher(value));
271/// }
272/// @endcode
273/// Then, we define a `Future` class, which holds a c-string `name`, char
274/// `callMonth`, and short `callYear`.
275/// @code
276/// class Future {
277/// @endcode
278/// This `class` identifies a future contract. It tracks the name, call month
279/// and year of the contract it represents, and allows equality comparison.
280/// @code
281/// // DATA
282/// const char *d_name; // held, not owned
283/// const char d_callMonth;
284/// const short d_callYear;
285///
286/// public:
287/// // CREATORS
288/// Future(const char *name, const char callMonth, const short callYear)
289/// : d_name(name), d_callMonth(callMonth), d_callYear(callYear)
290/// // Create a 'Future' object out of the specified 'name',
291/// // 'callMonth', and 'callYear'.
292/// {}
293///
294/// Future() : d_name(""), d_callMonth('\0'), d_callYear(0)
295/// // Create a 'Future' with default values.
296/// {}
297///
298/// // ACCESSORS
299/// const char * getMonth() const
300/// // Return the month that this future expires.
301/// {
302/// return &d_callMonth;
303/// }
304///
305/// const char * getName() const
306/// // Return the name of this future
307/// {
308/// return d_name;
309/// }
310///
311/// const short * getYear() const
312/// // Return the year that this future expires
313/// {
314/// return &d_callYear;
315/// }
316///
317/// bool operator==(const Future& rhs) const
318/// // Compare this to the specified 'other' object and return true if
319/// // they are equal
320/// {
321/// return (!strcmp(d_name, rhs.d_name)) &&
322/// d_callMonth == rhs.d_callMonth &&
323/// d_callYear == rhs.d_callYear;
324/// }
325/// };
326///
327/// bool operator!=(const Future& lhs, const Future& rhs)
328/// // Compare compare the specified 'lhs' and 'rhs' objects and return
329/// // true if they are not equal
330/// {
331/// return !(lhs == rhs);
332/// }
333/// @endcode
334/// Next, we need a hash functor for `Future`. We are going to use the
335/// `SpookyHashAlgorithm` because it is a fast, general purpose hashing
336/// algorithm that will provide an easy way to combine the attributes of
337/// `Future` objects that are salient to hashing into one reasonable hash that
338/// will distribute the items evenly throughout the hash table.
339/// @code
340/// struct HashFuture {
341/// // This struct is a functor that will apply the 'SpookyHashAlgorithm'
342/// // to objects of type 'Future'.
343///
344/// bsls::Types::Uint64 d_seed;
345///
346/// HashFuture()
347/// {
348/// // Generate random bits in 'd_seed' based on the time of day in
349/// // nanoseconds.
350///
351/// bsls::Types::Uint64 nano =
352/// bsls::SystemTime::nowMonotonicClock().totalNanoseconds();
353/// const int iterations = static_cast<int>(nano & 7) + 1;
354/// for (int ii = 0; ii < iterations; ++ii) {
355/// nano *= bsls::SystemTime::nowMonotonicClock().
356/// totalNanoseconds();
357/// nano += nano >> 32;
358/// }
359///
360/// BSLMF_ASSERT(sizeof(d_seed) <= sizeof(nano));
361///
362/// memcpy(&d_seed, &nano, sizeof(d_seed));
363/// }
364///
365/// // MANIPULATOR
366/// size_t operator()(const Future& future) const
367/// // Return the hash of the of the specified 'future'. Note that
368/// // this uses the 'SpookyHashAlgorithm' to quickly combine the
369/// // attributes of 'Future' objects that are salient to hashing into
370/// // a hash suitable for a hash table.
371/// {
372/// bslh::WyHashIncrementalAlgorithm hash(d_seed);
373///
374/// hash(future.getName(), strlen(future.getName()));
375/// hash(future.getMonth(), sizeof(char));
376/// hash(future.getYear(), sizeof(short));
377///
378/// return static_cast<size_t>(hash.computeHash());
379/// }
380/// };
381/// @endcode
382/// @}
383/** @} */
384/** @} */
385
386/** @addtogroup bsl
387 * @{
388 */
389/** @addtogroup bslh
390 * @{
391 */
392/** @addtogroup bslh_wyhashincrementalalgorithm
393 * @{
394 */
395
396#include <bslscm_version.h>
397
398#include <bslmf_assert.h>
400
401#include <bsls_assert.h>
402#include <bsls_byteorder.h>
403#include <bsls_keyword.h>
404#include <bsls_performancehint.h>
405#include <bsls_types.h>
406
407#include <algorithm>
408
409#include <stddef.h> // for 'size_t'
410#include <stdint.h> // for 'uint64_t'
411#include <string.h> // for 'memcpy'
412
413#if defined(_MSC_VER) && defined(_M_X64)
414#include <intrin.h>
415#pragma intrinsic(_umul128)
416#endif
417
418// protections that produce different results:
419
420//: 0 normal valid behavior
421//:
422//: 1 extra protection against entropy loss (probability=2^-63), aka. "blind
423//: multiplication"
424
425#undef BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR
426#define BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR 0
427
428//: 0 normal, real version of 64x64 -> 128 multiply, slow on 32 bit systems
429//:
430//: 1 not real multiply, faster on 32 bit systems but produces different
431//: results
432
433#undef BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_PSEUDO_MULTIPLY
434#define BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_PSEUDO_MULTIPLY 0
435
436
437namespace bslh {
438
439 // ======================================
440 // class bslh::WyHashIncrementalAlgorithm
441 // ======================================
442
443/// This class wraps an implementation of the "WyHash" hash algorithm in an
444/// interface that is usable in the modular hashing system in `bslh`.
445///
446/// See @ref bslh_wyhashincrementalalgorithm
448
449 private:
450 // PRIVATE TYPES
451 enum { k_PREPAD_LENGTH = 16, // See implementation
452 k_PREPAD_LENGTH_RAW = k_PREPAD_LENGTH - 1, // notes in the imp file
453 k_REPEAT_LENGTH = 48 };
454
455 public:
456 // TYPES
457
458 /// Typedef indicating the value type returned by this algorithm.
460
461 enum { k_SEED_LENGTH = sizeof(uint64_t) };
462
463 private:
464 // DATA
465 uint64_t d_initialSeed, d_seed, d_see1, d_see2; // seeds, state of the hash
466 // computation
467
468 bool d_last16AtEnd; // indicates that the last
469 // 16 bytes at the end of
470 // the repeat buf are valid
471
472 uint8_t d_buffer[k_PREPAD_LENGTH_RAW + k_REPEAT_LENGTH];
473 // prePad + repeat buffer,
474 // not including the first
475 // (never used) byte. See
476 // the implementation notes
477 // in the imp file
478
479 size_t d_totalLen; // total length of input so
480 // far
481
482 // CLASS DATA
483
484 // These values for 's_secret*' were copied directly from the original
485 // github source.
486
487 static const uint64_t s_secret0 = 0xa0761d6478bd642full;
488 static const uint64_t s_secret1 = 0xe7037ed1a0b428dbull;
489 static const uint64_t s_secret2 = 0x8ebc6af09c88c6e3ull;
490 static const uint64_t s_secret3 = 0x589965cc75374cc3ull;
491
492 private:
493 // NOT IMPLEMENTED
498
499 private:
500 // PRIVATE CLASS METHODS
501#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_PSEUDO_MULTIPLY
502 static uint64_t _wyrot(uint64_t x);
503 // Return the specified 'x' with the high- and low-order 32 bits
504 // swapped.
505#endif
506
507 /// Multiply the specified `*a_p` and `*b_p`, yielding a 128 bit result,
508 /// when `*b_p` will contain the high 64 bits and `*a_p` will contain
509 /// the low 64 bits of the result. This may be configured through
510 /// conditional switches to perform a faster function other than
511 /// multiply.
512 static void _wymum(uint64_t *a_p, uint64_t *b_p);
513
514 /// Do a 64x64 -> 128 bit multiply of the specified `a` and `b`, then
515 /// then return the bitwise-xor of the high and low 64-bits.
516 static uint64_t _wymix(uint64_t a, uint64_t b);
517
518 /// Read 8 bytes, native-endian. Note that `p` might not be aligned.
519 static uint64_t _wyr8(const uint8_t *p);
520
521 /// Read 4 bytes, native-endian, Note that `p` might not be aligned.
522 static uint64_t _wyr4(const uint8_t *p);
523
524 /// Read a mix of the specified `k` bytes beginning at the specified
525 /// `p`, where `k` is in the range `[ 1 .. 3 ]`.
526 static uint64_t _wyr3(const uint8_t *p, size_t k);
527
528 // PRIVATE MANIPULATORS
529
530 /// Return a ptr to the address at the specified `offset` after the
531 /// beginning of the `prepad` area of the buffer. The behavior is
532 /// undefined unless `1 <= offset`.
533 uint8_t *prePadAt(ptrdiff_t offset);
534
535 /// Process the specified `k_REPEAT_LENGTH`-byte `buffer`. Note that
536 /// this function is called only when there is additional input beyond
537 /// the buffer.
538 void process48ByteSection(const uint8_t *buffer);
539
540 /// Return a pointer to the beginning of the repeated buffer area.
541 uint8_t *repeatBufferBegin();
542
543 /// Return a pointer past the end of the buffer.
544 uint8_t *repeatBufferEnd();
545
546 public:
547 // CREATORS
548
549 /// Create a `WyHashIncrementalAlgorithm` using a default initial seed.
551
552 /// Create a `bslh::WyHashIncrementalAlgorithm`, seeded with the
553 /// specified `seed`.
554 explicit WyHashIncrementalAlgorithm(uint64_t seed);
555
556 /// Create a `bslh::WyHashIncrementalAlgorithm`, seeded with
557 /// `k_SEED_LENGTH` bytes of data starting at the specified `seed`.
558 explicit WyHashIncrementalAlgorithm(const char *seed);
559
561 // Destroy this object.
562
563 // MANIPULATORS
564
565 /// Incorporate the specified `data`, of at least the specified
566 /// `numBytes`, into the internal state of the hashing algorithm. Every
567 /// bit of data incorporated into the internal state of the algorithm
568 /// will contribute to the final hash produced by `computeHash()`. The
569 /// same hash value will be produced regardless of whether a sequence of
570 /// bytes is passed in all at once or through multiple calls to this
571 /// member function. Input where `numBytes` is 0 will have no effect on
572 /// the internal state of the algorithm. The behaviour is undefined
573 /// unless `data` points to a valid memory location with at least
574 /// `numBytes` bytes of initialized memory or `numBytes` is zero.
575 void operator()(const void *data, size_t numBytes);
576
577 /// Return the finalized version of the hash that has been accumulated.
578 /// Note that this changes the internal state of the object, so calling
579 /// `computeHash()` multiple times in a row will return different
580 /// results, and only the first result returned will match the expected
581 /// result of the algorithm. Also note that a value will be returned,
582 /// even if data has not been passed into `operator()`
584};
585
586// ============================================================================
587// INLINE DEFINITIONS
588// ============================================================================
589
590 // --------------------------
591 // WyHashIncrementalAlgorithm
592 // --------------------------
593
594// PRIVATE CLASS METHODS
595#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_PSEUDO_MULTIPLY
596inline
597uint64_t WyHashIncrementalAlgorithm::_wyrot(uint64_t x)
598{
599 return (x >> 32) | (x << 32);
600}
601#endif
602
603inline
604void WyHashIncrementalAlgorithm::_wymum(uint64_t *a_p, uint64_t *b_p)
605{
606#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_PSEUDO_MULTIPLY
607 const uint64_t hh = (*a_p >> 32) * (*b_p >> 32);
608 const uint64_t hl = (*a_p >> 32) * static_cast<uint32_t>(*b_p);
609 const uint64_t lh = static_cast<uint32_t>(*a_p) * (*b_p >> 32);
610 const uint64_t ll = static_cast<uint64_t>(static_cast<uint32_t>(*a_p)) *
611 static_cast<uint32_t>(*b_p);
612
613#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR
614 // pseudo munge (not real multiply) -> xor
615
616 *a_p ^= _wyrot(hl) ^ hh;
617 *b_p ^= _wyrot(lh) ^ ll;
618#else
619 // pseudo munge (not real multiply)
620
621 *a_p = _wyrot(hl) ^ hh;
622 *b_p = _wyrot(lh) ^ ll;
623#endif
624#elif defined(__SIZEOF_INT128__)
625 __uint128_t r = *a_p;
626 r *= *b_p;
627
628#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR
629 // multiply -> xor
630
631 *a_p ^= static_cast<uint64_t>(r);
632 *b_p ^= static_cast<uint64_t>(r >> 64);
633#else
634 // multiply
635
636 *a_p = static_cast<uint64_t>(r);
637 *b_p = static_cast<uint64_t>(r >> 64);
638#endif
639#elif defined(_MSC_VER) && defined(_M_X64)
640#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR
641 // multiply -> xor
642
643 uint64_t a, b;
644 a = _umul128(*a_p, *b_p, &b);
645 *a_p ^= a;
646 *b_p ^= b;
647#else
648 // multiply
649
650 *a_p = _umul128(*a_p, *b_p, b_p);
651#endif
652#else
653 const uint64_t ha = *a_p >> 32, hb = *b_p >> 32;
654 const uint64_t la = static_cast<uint32_t>(*a_p);
655 const uint64_t lb = static_cast<uint32_t>(*b_p);
656
657 const uint64_t rh = ha * hb, rl = la * lb;
658 const uint64_t rm0 = ha * lb, rm1 = hb * la;
659 const uint64_t t = rl + (rm0 << 32);
660
661 const uint64_t lo = t + (rm1 << 32);
662 const uint64_t hi = rh + (rm0 >> 32) + (rm1 >> 32) + (t < rl) + (lo < t);
663
664#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR
665 // multiply -> xor
666
667 *a_p ^= lo;
668 *b_p ^= hi;
669#else
670 // multiply
671
672 *a_p = lo;
673 *b_p = hi;
674#endif
675#endif
676}
677
678//multiply and xor mix function, aka MUM
679inline
680uint64_t WyHashIncrementalAlgorithm::_wymix(uint64_t a, uint64_t b)
681{
682 _wymum(&a, &b);
683 return a ^ b;
684}
685
686//read functions
687inline
688uint64_t WyHashIncrementalAlgorithm::_wyr8(const uint8_t *p)
689{
690 uint64_t v;
691 memcpy(&v, p, 8);
692 return v;
693}
694
695inline
696uint64_t WyHashIncrementalAlgorithm::_wyr4(const uint8_t *p)
697{
698 uint32_t v;
699 memcpy(&v, p, 4);
700 return v;
701}
702
703/// Read a mix of the `k` bytes beginning at `p`, where `k` is in the range
704/// `[ 1 .. 3 ]`.
705inline
706uint64_t WyHashIncrementalAlgorithm::_wyr3(const uint8_t *p, size_t k)
707{
708 BSLS_ASSERT_SAFE(1 <= k && k <= 3);
709
710 return (static_cast<uint64_t>(p[0]) << 16) |
711 (static_cast<uint64_t>(p[k >> 1]) << 8) |
712 p[k - 1];
713}
714
715// PRIVATE MANIPULATORS
716inline
717uint8_t *WyHashIncrementalAlgorithm::prePadAt(ptrdiff_t offset)
718{
719 BSLMF_ASSERT(sizeof(d_last16AtEnd) == 1); // see implementation doc
720 BSLS_ASSERT_SAFE(1 <= offset);
721
722 return d_buffer + offset - 1;
723}
724
725inline
726void WyHashIncrementalAlgorithm::process48ByteSection(const uint8_t *buffer)
727{
728 d_seed = _wymix(_wyr8(buffer) ^ s_secret1,
729 _wyr8(buffer + 8) ^ d_seed);
730 d_see1 = _wymix(_wyr8(buffer + 16) ^ s_secret2,
731 _wyr8(buffer + 24) ^ d_see1);
732 d_see2 = _wymix(_wyr8(buffer + 32) ^ s_secret3,
733 _wyr8(buffer + 40) ^ d_see2);
734}
735
736inline
737uint8_t *WyHashIncrementalAlgorithm::repeatBufferBegin()
738{
739 return d_buffer + k_PREPAD_LENGTH_RAW;
740}
741
742inline
743uint8_t *WyHashIncrementalAlgorithm::repeatBufferEnd()
744{
745 return d_buffer + sizeof(d_buffer);
746}
747
748// CREATORS
749inline
751: d_initialSeed(0x50defacedfacade5ULL)
752, d_last16AtEnd(false)
753, d_totalLen(0)
754{
755 BSLMF_ASSERT(sizeof(*this) == 13 * sizeof(uint64_t) ||
756 sizeof(*this) == 12 * sizeof(uint64_t) + sizeof(size_t));
757
758 d_seed = d_initialSeed ^ s_secret0;
759}
760
761inline
763: d_initialSeed(seed)
764, d_last16AtEnd(false)
765, d_totalLen(0)
766{
767 BSLMF_ASSERT(sizeof(d_initialSeed) == sizeof(seed));
768 d_seed = d_initialSeed ^ s_secret0;
769}
770
771inline
773: d_last16AtEnd(false)
774, d_totalLen(0)
775{
776 BSLMF_ASSERT(sizeof(d_initialSeed) == k_SEED_LENGTH);
777
778 memcpy(&d_initialSeed, seed, sizeof(d_initialSeed));
779
780 d_seed = d_initialSeed ^ s_secret0;
781}
782
783// MANIPULATORS
784inline
785void WyHashIncrementalAlgorithm::operator()(const void *data, size_t numBytes)
786{
787 if (0 == numBytes) {
788 // In all cases when '0 == numBytes', all we do is a 'memcpy' of 0
789 // length, which is a no-op. In the cases where '0 == data' (for
790 // example, hashing a default-constructed 'bsl::string_view') this
791 // 'memcpy' gets 'p' (0) passed to the second arg, which is technically
792 // UB. To avoid this UB, just return.
793
794 return; // RETURN
795 }
796 else {
797 BSLS_ASSERT_SAFE(0 != data);
798 }
799
800 const uint8_t *p = static_cast<const uint8_t *>(data);
801 const uint8_t *end = p + numBytes;
802
803 const size_t origLen = d_totalLen;
804 size_t repeatBufNumBytes = origLen % k_REPEAT_LENGTH;
805 if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!repeatBufNumBytes && origLen)) {
807
808 repeatBufNumBytes = k_REPEAT_LENGTH;
809 }
810
811 d_totalLen = origLen + numBytes;
812
813 if (0 != repeatBufNumBytes) {
814 const ptrdiff_t remainingSpaceInBuf = k_REPEAT_LENGTH -
815 repeatBufNumBytes;
817 remainingSpaceInBuf)) {
818 memcpy(repeatBufferBegin() + repeatBufNumBytes, p, end - p);
819
820 // Leave 'd_last16AtEnd' alone.
821
822 return; // RETURN
823 }
824 else {
826
827 if (origLen <= k_REPEAT_LENGTH) {
828 d_see1 = d_see2 = d_seed;
829 }
830
831 memcpy(repeatBufferBegin() + repeatBufNumBytes,
832 p,
833 remainingSpaceInBuf);
834
835 p += remainingSpaceInBuf;
836 process48ByteSection(repeatBufferBegin());
837
838 d_last16AtEnd = true;
839 }
840 }
841
842 if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(k_REPEAT_LENGTH < end - p)) {
844
845 if (0 == origLen) {
846 d_see1 = d_see2 = d_seed;
847 }
848
849 do {
850 process48ByteSection(p);
851 p += k_REPEAT_LENGTH;
852 } while (k_REPEAT_LENGTH < end - p);
853
854 d_last16AtEnd = false;
855
856 const ptrdiff_t remOffset = end - p;
857 if (remOffset < 16) {
858 BSLS_ASSERT_SAFE(0 < remOffset);
859
860 // We say 'p + remOffset - 16' rather than 'end - 16' below because
861 // the latter caused an inaccurate warning which could not be
862 // silenced via pragmas.
863
864 memcpy(prePadAt(remOffset), p + remOffset - 16, 16);
865
866 return; // RETURN
867 }
868 }
869
870 memcpy(repeatBufferBegin(), p, end - p);
871}
872
873inline
876{
877 BSLMF_ASSERT(sizeof(result_type) == sizeof(d_seed));
878
879 uint8_t *p = repeatBufferBegin();
880
881 uint64_t a, b, seed = d_seed;
882
883 const size_t len = d_totalLen;
886 const size_t subLen = (len >> 3) << 2;
887
888 a = (_wyr4(p) << 32) | _wyr4(p + subLen);
889 b = (_wyr4(p + len - 4) << 32) | _wyr4(p + len - 4 - subLen);
890 }
891 else if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(len > 0)) {
892 a = _wyr3(p, len);
893 b = 0;
894 }
895 else {
897
898 a = b = 0;
899 }
900 }
901 else {
903
904 size_t totalLenRemainder = len % k_REPEAT_LENGTH;
905 if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(0 == totalLenRemainder)) {
907
908 totalLenRemainder = k_REPEAT_LENGTH;
909 }
910 uint8_t *end = p + totalLenRemainder;
911
912 if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(k_REPEAT_LENGTH < len)) {
914
915 seed ^= d_see1 ^ d_see2;
916 }
917
918 while (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(end - p > 16)) {
920
921 seed = _wymix(_wyr8(p) ^ s_secret1, _wyr8(p + 8) ^ seed);
922 p += 16;
923 }
924
925 const ptrdiff_t offset = end - repeatBufferBegin() - k_PREPAD_LENGTH;
927 d_last16AtEnd)) {
929
930 BSLS_ASSERT_SAFE(k_REPEAT_LENGTH < len);
931 memcpy(repeatBufferBegin() + offset,
932 repeatBufferEnd() + offset,
933 -offset);
934 }
935
936 a = _wyr8(end - 16);
937 b = _wyr8(end - 8);
938 }
939
940 return d_initialSeed ^ _wymix(s_secret1 ^ len,
941 _wymix(a ^ s_secret1, b ^ seed));
942}
943
944} // close package namespace
945
946
947// ============================================================================
948// TYPE TRAITS
949// ============================================================================
950
951
952namespace bslmf {
953template <>
957} // close namespace bslmf
958
959
960#undef BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_PSEUDO_MULTIPLY
961#undef BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR
962
963#endif
964
965// ----------------------------------------------------------------------------
966// Copyright 2022 Bloomberg Finance L.P.
967//
968// Licensed under the Apache License, Version 2.0 (the "License");
969// you may not use this file except in compliance with the License.
970// You may obtain a copy of the License at
971//
972// http://www.apache.org/licenses/LICENSE-2.0
973//
974// Unless required by applicable law or agreed to in writing, software
975// distributed under the License is distributed on an "AS IS" BASIS,
976// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
977// See the License for the specific language governing permissions and
978// limitations under the License.
979// ----------------------------- END-OF-FILE ----------------------------------
980
981/** @} */
982/** @} */
983/** @} */
Definition bslh_wyhashincrementalalgorithm.h:447
result_type computeHash()
Definition bslh_wyhashincrementalalgorithm.h:875
void operator()(const void *data, size_t numBytes)
Definition bslh_wyhashincrementalalgorithm.h:785
WyHashIncrementalAlgorithm()
Create a WyHashIncrementalAlgorithm using a default initial seed.
Definition bslh_wyhashincrementalalgorithm.h:750
@ k_SEED_LENGTH
Definition bslh_wyhashincrementalalgorithm.h:461
bsls::Types::Uint64 result_type
Typedef indicating the value type returned by this algorithm.
Definition bslh_wyhashincrementalalgorithm.h:459
#define BSLMF_ASSERT(expr)
Definition bslmf_assert.h:229
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1762
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
#define BSLS_KEYWORD_DELETED
Definition bsls_keyword.h:609
#define BSLS_PERFORMANCEHINT_PREDICT_LIKELY(expr)
Definition bsls_performancehint.h:451
#define BSLS_PERFORMANCEHINT_UNLIKELY_HINT
Definition bsls_performancehint.h:484
#define BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(expr)
Definition bsls_performancehint.h:452
Definition bslh_defaulthashalgorithm.h:339
Definition bdlbb_blob.h:576
Definition bslmf_isbitwisecopyable.h:298
unsigned long long Uint64
Definition bsls_types.h:137