BDE 4.39.x 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 <stddef.h> // for 'size_t'
408#include <stdint.h> // for 'uint64_t'
409#include <string.h> // for 'memcpy'
410
411#if defined(_MSC_VER) && defined(_M_X64)
412# include <intrin.h>
413# pragma intrinsic(_umul128)
414#endif
415
416#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
417# include <algorithm>
418#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
419
420// protections that produce different results:
421
422//: 0 normal valid behavior
423//:
424//: 1 extra protection against entropy loss (probability=2^-63), aka. "blind
425//: multiplication"
426
427#undef BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR
428#define BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR 0
429
430//: 0 normal, real version of 64x64 -> 128 multiply, slow on 32 bit systems
431//:
432//: 1 not real multiply, faster on 32 bit systems but produces different
433//: results
434
435#undef BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_PSEUDO_MULTIPLY
436#define BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_PSEUDO_MULTIPLY 0
437
438
439namespace bslh {
440
441 // ======================================
442 // class bslh::WyHashIncrementalAlgorithm
443 // ======================================
444
445/// This class wraps an implementation of the "WyHash" hash algorithm in an
446/// interface that is usable in the modular hashing system in `bslh`.
447///
448/// See @ref bslh_wyhashincrementalalgorithm
450
451 private:
452 // PRIVATE TYPES
453 enum { k_PREPAD_LENGTH = 16, // See implementation
454 k_PREPAD_LENGTH_RAW = k_PREPAD_LENGTH - 1, // notes in the imp file
455 k_REPEAT_LENGTH = 48 };
456
457 public:
458 // TYPES
459
460 /// Typedef indicating the value type returned by this algorithm.
462
463 enum { k_SEED_LENGTH = sizeof(uint64_t) };
464
465 private:
466 // DATA
467 uint64_t d_initialSeed, d_seed, d_see1, d_see2; // seeds, state of the hash
468 // computation
469
470 bool d_last16AtEnd; // indicates that the last
471 // 16 bytes at the end of
472 // the repeat buf are valid
473
474 uint8_t d_buffer[k_PREPAD_LENGTH_RAW + k_REPEAT_LENGTH];
475 // prePad + repeat buffer,
476 // not including the first
477 // (never used) byte. See
478 // the implementation notes
479 // in the imp file
480
481 size_t d_totalLen; // total length of input so
482 // far
483
484 // CLASS DATA
485
486 // These values for 's_secret*' were copied directly from the original
487 // github source.
488
489 static const uint64_t s_secret0 = 0xa0761d6478bd642full;
490 static const uint64_t s_secret1 = 0xe7037ed1a0b428dbull;
491 static const uint64_t s_secret2 = 0x8ebc6af09c88c6e3ull;
492 static const uint64_t s_secret3 = 0x589965cc75374cc3ull;
493
494 private:
495 // NOT IMPLEMENTED
500
501 private:
502 // PRIVATE CLASS METHODS
503#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_PSEUDO_MULTIPLY
504 static uint64_t _wyrot(uint64_t x);
505 // Return the specified 'x' with the high- and low-order 32 bits
506 // swapped.
507#endif
508
509 /// Multiply the specified `*a_p` and `*b_p`, yielding a 128 bit result,
510 /// when `*b_p` will contain the high 64 bits and `*a_p` will contain
511 /// the low 64 bits of the result. This may be configured through
512 /// conditional switches to perform a faster function other than
513 /// multiply.
514 static void _wymum(uint64_t *a_p, uint64_t *b_p);
515
516 /// Do a 64x64 -> 128 bit multiply of the specified `a` and `b`, then
517 /// then return the bitwise-xor of the high and low 64-bits.
518 static uint64_t _wymix(uint64_t a, uint64_t b);
519
520 /// Read 8 bytes, native-endian.
521 /// \note Note that `p` might not be aligned.
522 static uint64_t _wyr8(const uint8_t *p);
523
524 /// Read 4 bytes, native-endian,
525 /// \note Note that `p` might not be aligned.
526 static uint64_t _wyr4(const uint8_t *p);
527
528 /// Read a mix of the specified `k` bytes beginning at the specified
529 /// `p`, where `k` is in the range `[ 1 .. 3 ]`.
530 static uint64_t _wyr3(const uint8_t *p, size_t k);
531
532 // PRIVATE MANIPULATORS
533
534 /// Return a ptr to the address at the specified `offset` after the
535 /// beginning of the `prepad` area of the buffer.
536 ///
537 /// \pre The behavior is undefined unless `1 <= offset`.
538 uint8_t *prePadAt(ptrdiff_t offset);
539
540 /// Process the specified `k_REPEAT_LENGTH`-byte `buffer`.
541 ///
542 /// \note Note that this function is called only when there is additional input beyond
543 /// the buffer.
544 void process48ByteSection(const uint8_t *buffer);
545
546 /// Return a pointer to the beginning of the repeated buffer area.
547 uint8_t *repeatBufferBegin();
548
549 /// Return a pointer past the end of the buffer.
550 uint8_t *repeatBufferEnd();
551
552 public:
553 // CREATORS
554
555 /// Create a `WyHashIncrementalAlgorithm` using a default initial seed.
557
558 /// Create a `bslh::WyHashIncrementalAlgorithm`, seeded with the
559 /// specified `seed`.
560 explicit WyHashIncrementalAlgorithm(uint64_t seed);
561
562 /// Create a `bslh::WyHashIncrementalAlgorithm`, seeded with
563 /// `k_SEED_LENGTH` bytes of data starting at the specified `seed`.
564 explicit WyHashIncrementalAlgorithm(const char *seed);
565
566 /// Create a `WyHashIncrementalAlgorithm` object having the same
567 /// accumulated state as the specified `original`.
569 // = default;
570
571 /// Destroy this object.
572 ~WyHashIncrementalAlgorithm() = default;
573
574 // MANIPULATORS
575
576 /// Assign to this object the value of the accumulates state of the
577 /// specified `rhs`, and return a reference providing modifiable access to
578 /// this object.
580 // const WyHashIncrementalAlgorithm& rhs);
581
582 /// Incorporate the specified `data`, of at least the specified
583 /// `numBytes`, into the internal state of the hashing algorithm. Every
584 /// bit of data incorporated into the internal state of the algorithm
585 /// will contribute to the final hash produced by `computeHash()`. The
586 /// same hash value will be produced regardless of whether a sequence of
587 /// bytes is passed in all at once or through multiple calls to this
588 /// member function. Input where `numBytes` is 0 will have no effect on
589 /// the internal state of the algorithm.
590 ///
591 /// \pre The behaviour is undefined unless `data` points to a valid memory location with at least
592 /// `numBytes` bytes of initialized memory or `numBytes` is zero.
593 void operator()(const void *data, size_t numBytes);
594
595 /// Return the finalized version of the hash that has been accumulated.
596 ///
597 /// \note Note that this changes the internal state of the object, so calling
598 /// `computeHash()` multiple times in a row will return different
599 /// results, and only the first result returned will match the expected
600 /// result of the algorithm. Also note that a value will be returned,
601 /// even if data has not been passed into `operator()`
602 result_type computeHash();
603};
604
605// ============================================================================
606// INLINE DEFINITIONS
607// ============================================================================
608
609 // --------------------------
610 // WyHashIncrementalAlgorithm
611 // --------------------------
612
613// PRIVATE CLASS METHODS
614#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_PSEUDO_MULTIPLY
615inline
616uint64_t WyHashIncrementalAlgorithm::_wyrot(uint64_t x)
617{
618 return (x >> 32) | (x << 32);
619}
620#endif
621
622inline
623void WyHashIncrementalAlgorithm::_wymum(uint64_t *a_p, uint64_t *b_p)
624{
625#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_PSEUDO_MULTIPLY
626 const uint64_t hh = (*a_p >> 32) * (*b_p >> 32);
627 const uint64_t hl = (*a_p >> 32) * static_cast<uint32_t>(*b_p);
628 const uint64_t lh = static_cast<uint32_t>(*a_p) * (*b_p >> 32);
629 const uint64_t ll = static_cast<uint64_t>(static_cast<uint32_t>(*a_p)) *
630 static_cast<uint32_t>(*b_p);
631
632#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR
633 // pseudo munge (not real multiply) -> xor
634
635 *a_p ^= _wyrot(hl) ^ hh;
636 *b_p ^= _wyrot(lh) ^ ll;
637#else
638 // pseudo munge (not real multiply)
639
640 *a_p = _wyrot(hl) ^ hh;
641 *b_p = _wyrot(lh) ^ ll;
642#endif
643#elif defined(__SIZEOF_INT128__)
644 __uint128_t r = *a_p;
645 r *= *b_p;
646
647#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR
648 // multiply -> xor
649
650 *a_p ^= static_cast<uint64_t>(r);
651 *b_p ^= static_cast<uint64_t>(r >> 64);
652#else
653 // multiply
654
655 *a_p = static_cast<uint64_t>(r);
656 *b_p = static_cast<uint64_t>(r >> 64);
657#endif
658#elif defined(_MSC_VER) && defined(_M_X64)
659#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR
660 // multiply -> xor
661
662 uint64_t a, b;
663 a = _umul128(*a_p, *b_p, &b);
664 *a_p ^= a;
665 *b_p ^= b;
666#else
667 // multiply
668
669 *a_p = _umul128(*a_p, *b_p, b_p);
670#endif
671#else
672 const uint64_t ha = *a_p >> 32, hb = *b_p >> 32;
673 const uint64_t la = static_cast<uint32_t>(*a_p);
674 const uint64_t lb = static_cast<uint32_t>(*b_p);
675
676 const uint64_t rh = ha * hb, rl = la * lb;
677 const uint64_t rm0 = ha * lb, rm1 = hb * la;
678 const uint64_t t = rl + (rm0 << 32);
679
680 const uint64_t lo = t + (rm1 << 32);
681 const uint64_t hi = rh + (rm0 >> 32) + (rm1 >> 32) + (t < rl) + (lo < t);
682
683#if BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR
684 // multiply -> xor
685
686 *a_p ^= lo;
687 *b_p ^= hi;
688#else
689 // multiply
690
691 *a_p = lo;
692 *b_p = hi;
693#endif
694#endif
695}
696
697//multiply and xor mix function, aka MUM
698inline
699uint64_t WyHashIncrementalAlgorithm::_wymix(uint64_t a, uint64_t b)
700{
701 _wymum(&a, &b);
702 return a ^ b;
703}
704
705//read functions
706inline
707uint64_t WyHashIncrementalAlgorithm::_wyr8(const uint8_t *p)
708{
709 uint64_t v;
710 memcpy(&v, p, 8);
711 return v;
712}
713
714inline
715uint64_t WyHashIncrementalAlgorithm::_wyr4(const uint8_t *p)
716{
717 uint32_t v;
718 memcpy(&v, p, 4);
719 return v;
720}
721
722/// Read a mix of the `k` bytes beginning at `p`, where `k` is in the range
723/// `[ 1 .. 3 ]`.
724inline
725uint64_t WyHashIncrementalAlgorithm::_wyr3(const uint8_t *p, size_t k)
726{
727 BSLS_ASSERT_SAFE(1 <= k && k <= 3);
728
729 return (static_cast<uint64_t>(p[0]) << 16) |
730 (static_cast<uint64_t>(p[k >> 1]) << 8) |
731 p[k - 1];
732}
733
734// PRIVATE MANIPULATORS
735inline
736uint8_t *WyHashIncrementalAlgorithm::prePadAt(ptrdiff_t offset)
737{
738 BSLMF_ASSERT(sizeof(d_last16AtEnd) == 1); // see implementation doc
739 BSLS_ASSERT_SAFE(1 <= offset);
740
741 return d_buffer + offset - 1;
742}
743
744inline
745void WyHashIncrementalAlgorithm::process48ByteSection(const uint8_t *buffer)
746{
747 d_seed = _wymix(_wyr8(buffer) ^ s_secret1,
748 _wyr8(buffer + 8) ^ d_seed);
749 d_see1 = _wymix(_wyr8(buffer + 16) ^ s_secret2,
750 _wyr8(buffer + 24) ^ d_see1);
751 d_see2 = _wymix(_wyr8(buffer + 32) ^ s_secret3,
752 _wyr8(buffer + 40) ^ d_see2);
753}
754
755inline
756uint8_t *WyHashIncrementalAlgorithm::repeatBufferBegin()
757{
758 return d_buffer + k_PREPAD_LENGTH_RAW;
759}
760
761inline
762uint8_t *WyHashIncrementalAlgorithm::repeatBufferEnd()
763{
764 return d_buffer + sizeof(d_buffer);
765}
766
767// CREATORS
768inline
770: d_initialSeed(0x50defacedfacade5ULL)
771, d_last16AtEnd(false)
772, d_totalLen(0)
773{
774 BSLMF_ASSERT(sizeof(*this) == 13 * sizeof(uint64_t) ||
775 sizeof(*this) == 12 * sizeof(uint64_t) + sizeof(size_t));
776
777 d_see1 = d_see2 = d_seed = d_initialSeed ^ s_secret0;
778}
779
780inline
782: d_initialSeed(seed)
783, d_last16AtEnd(false)
784, d_totalLen(0)
785{
786 BSLMF_ASSERT(sizeof(d_initialSeed) == sizeof(seed));
787 d_see1 = d_see2 = d_seed = d_initialSeed ^ s_secret0;
788}
789
790inline
792: d_last16AtEnd(false)
793, d_totalLen(0)
794{
795 BSLMF_ASSERT(sizeof(d_initialSeed) == k_SEED_LENGTH);
796
797 memcpy(&d_initialSeed, seed, sizeof(d_initialSeed));
798
799 d_see1 = d_see2 = d_seed = d_initialSeed ^ s_secret0;
800}
801
802// MANIPULATORS
803inline
804void WyHashIncrementalAlgorithm::operator()(const void *data, size_t numBytes)
805{
806 if (0 == numBytes) {
807 // In all cases when '0 == numBytes', all we do is a 'memcpy' of 0
808 // length, which is a no-op. In the cases where '0 == data' (for
809 // example, hashing a default-constructed 'bsl::string_view') this
810 // 'memcpy' gets 'p' (0) passed to the second arg, which is technically
811 // UB. To avoid this UB, just return.
812
813 return; // RETURN
814 }
815 else {
816 BSLS_ASSERT_SAFE(0 != data);
817 }
818
819 const uint8_t *p = static_cast<const uint8_t *>(data);
820 const uint8_t *end = p + numBytes;
821
822 const size_t origLen = d_totalLen;
823 size_t repeatBufNumBytes = origLen % k_REPEAT_LENGTH;
824 if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!repeatBufNumBytes && origLen)) {
826
827 repeatBufNumBytes = k_REPEAT_LENGTH;
828 }
829
830 d_totalLen = origLen + numBytes;
831
832 if (0 != repeatBufNumBytes) {
833 const ptrdiff_t remainingSpaceInBuf = k_REPEAT_LENGTH -
834 repeatBufNumBytes;
836 remainingSpaceInBuf)) {
837 memcpy(repeatBufferBegin() + repeatBufNumBytes, p, end - p);
838
839 // Leave 'd_last16AtEnd' alone.
840
841 return; // RETURN
842 }
843 else {
845
846 memcpy(repeatBufferBegin() + repeatBufNumBytes,
847 p,
848 remainingSpaceInBuf);
849
850 p += remainingSpaceInBuf;
851 process48ByteSection(repeatBufferBegin());
852
853 d_last16AtEnd = true;
854 }
855 }
856
857 if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(k_REPEAT_LENGTH < end - p)) {
859
860 do {
861 process48ByteSection(p);
862 p += k_REPEAT_LENGTH;
863 } while (k_REPEAT_LENGTH < end - p);
864
865 d_last16AtEnd = false;
866
867 const ptrdiff_t remOffset = end - p;
868 if (remOffset < 16) {
869 BSLS_ASSERT_SAFE(0 < remOffset);
870
871 // We say 'p + remOffset - 16' rather than 'end - 16' below because
872 // the latter caused an inaccurate warning which could not be
873 // silenced via pragmas.
874
875 memcpy(prePadAt(remOffset), p + remOffset - 16, 16);
876
877 return; // RETURN
878 }
879 }
880
881 memcpy(repeatBufferBegin(), p, end - p);
882}
883
884inline
886WyHashIncrementalAlgorithm::computeHash()
887{
888 BSLMF_ASSERT(sizeof(result_type) == sizeof(d_seed));
889
890 uint8_t *p = repeatBufferBegin();
891
892 uint64_t a, b, seed = d_seed;
893
894 const size_t len = d_totalLen;
897 const size_t subLen = (len >> 3) << 2;
898
899 a = (_wyr4(p) << 32) | _wyr4(p + subLen);
900 b = (_wyr4(p + len - 4) << 32) | _wyr4(p + len - 4 - subLen);
901 }
902 else if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(len > 0)) {
903 a = _wyr3(p, len);
904 b = 0;
905 }
906 else {
908
909 a = b = 0;
910 }
911 }
912 else {
914
915 size_t totalLenRemainder = len % k_REPEAT_LENGTH;
916 if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(0 == totalLenRemainder)) {
918
919 totalLenRemainder = k_REPEAT_LENGTH;
920 }
921 uint8_t *end = p + totalLenRemainder;
922
923 if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(k_REPEAT_LENGTH < len)) {
925
926 seed ^= d_see1 ^ d_see2;
927 }
928
929 while (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(end - p > 16)) {
931
932 seed = _wymix(_wyr8(p) ^ s_secret1, _wyr8(p + 8) ^ seed);
933 p += 16;
934 }
935
936 const ptrdiff_t offset = end - repeatBufferBegin() - k_PREPAD_LENGTH;
938 d_last16AtEnd)) {
940
941 BSLS_ASSERT_SAFE(k_REPEAT_LENGTH < len);
942 memcpy(repeatBufferBegin() + offset,
943 repeatBufferEnd() + offset,
944 -offset);
945 }
946
947 a = _wyr8(end - 16);
948 b = _wyr8(end - 8);
949 }
950
951 return d_initialSeed ^ _wymix(s_secret1 ^ len,
952 _wymix(a ^ s_secret1, b ^ seed));
953}
954
955} // close package namespace
956
957
958// ============================================================================
959// TYPE TRAITS
960// ============================================================================
961
962
963namespace bslmf {
964template <>
968} // close namespace bslmf
969
970
971#undef BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_PSEUDO_MULTIPLY
972#undef BSLH_WYHASHINCREMENTALALGORITHM_WYMUM_XOR
973
974#endif
975
976// ----------------------------------------------------------------------------
977// Copyright 2022 Bloomberg Finance L.P.
978//
979// Licensed under the Apache License, Version 2.0 (the "License");
980// you may not use this file except in compliance with the License.
981// You may obtain a copy of the License at
982//
983// http://www.apache.org/licenses/LICENSE-2.0
984//
985// Unless required by applicable law or agreed to in writing, software
986// distributed under the License is distributed on an "AS IS" BASIS,
987// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
988// See the License for the specific language governing permissions and
989// limitations under the License.
990// ----------------------------- END-OF-FILE ----------------------------------
991
992/** @} */
993/** @} */
994/** @} */
Definition bslh_wyhashincrementalalgorithm.h:449
WyHashIncrementalAlgorithm()
Create a WyHashIncrementalAlgorithm using a default initial seed.
Definition bslh_wyhashincrementalalgorithm.h:769
@ k_SEED_LENGTH
Definition bslh_wyhashincrementalalgorithm.h:463
WyHashIncrementalAlgorithm(const WyHashIncrementalAlgorithm &original) ~WyHashIncrementalAlgorithm()=default
Destroy this object.
bsls::Types::Uint64 result_type
Typedef indicating the value type returned by this algorithm.
Definition bslh_wyhashincrementalalgorithm.h:461
#define BSLMF_ASSERT(expr)
Definition bslmf_assert.h:231
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1917
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
#define BSLS_KEYWORD_DELETED
Definition bsls_keyword.h:651
#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
T::iterator end(T &container)
Definition bslstl_iterator.h:1621
BSLS_KEYWORD_CONSTEXPR CONTAINER::value_type * data(CONTAINER &container)
Definition bslstl_iterator.h:1325
Definition bslh_defaulthashalgorithm.h:339
Definition bdlbb_blob.h:579
Definition bslmf_isbitwisecopyable.h:298
unsigned long long Uint64
Definition bsls_types.h:139