1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. // A pure JS implementation of a string hashing function. We do not use it for
    
  11. // security or obfuscation purposes, only to create compact hashes. So we
    
  12. // prioritize speed over collision avoidance. For example, we use this to hash
    
  13. // the component key path used by useFormState for MPA-style submissions.
    
  14. //
    
  15. // In environments where built-in hashing functions are available, we prefer
    
  16. // those instead. Like Node's crypto module, or Bun.hash. Unfortunately this
    
  17. // does not include the web standard crypto API because those methods are all
    
  18. // async. For our purposes, we need it to be sync because the cost of context
    
  19. // switching is too high to be worth it.
    
  20. //
    
  21. // The most popular hashing algorithm that meets these requirements in the JS
    
  22. // ecosystem is MurmurHash3, and almost all implementations I could find used
    
  23. // some version of the implementation by Gary Court inlined below.
    
  24. 
    
  25. export function createFastHashJS(key: string): number {
    
  26.   return murmurhash3_32_gc(key, 0);
    
  27. }
    
  28. 
    
  29. /* eslint-disable prefer-const, no-fallthrough */
    
  30. 
    
  31. /**
    
  32.  * @license
    
  33.  *
    
  34.  * JS Implementation of MurmurHash3 (r136) (as of May 20, 2011)
    
  35.  *
    
  36.  * Copyright (c) 2011 Gary Court
    
  37.  * Permission is hereby granted, free of charge, to any person obtaining a copy
    
  38.  * of this software and associated documentation files (the "Software"), to deal
    
  39.  * in the Software without restriction, including without limitation the rights
    
  40.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    
  41.  * copies of the Software, and to permit persons to whom the Software is
    
  42.  * furnished to do so, subject to the following conditions:
    
  43.  *
    
  44.  * The above copyright notice and this permission notice shall be included in
    
  45.  * all copies or substantial portions of the Software.
    
  46.  *
    
  47.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    
  48.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    
  49.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    
  50.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    
  51.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    
  52.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    
  53.  * SOFTWARE.
    
  54.  */
    
  55. 
    
  56. function murmurhash3_32_gc(key: string, seed: number): number {
    
  57.   let remainder, bytes, h1, h1b, c1, c2, k1, i;
    
  58. 
    
  59.   remainder = key.length & 3; // key.length % 4
    
  60.   bytes = key.length - remainder;
    
  61.   h1 = seed;
    
  62.   c1 = 0xcc9e2d51;
    
  63.   c2 = 0x1b873593;
    
  64.   i = 0;
    
  65. 
    
  66.   while (i < bytes) {
    
  67.     k1 =
    
  68.       (key.charCodeAt(i) & 0xff) |
    
  69.       ((key.charCodeAt(++i) & 0xff) << 8) |
    
  70.       ((key.charCodeAt(++i) & 0xff) << 16) |
    
  71.       ((key.charCodeAt(++i) & 0xff) << 24);
    
  72.     ++i;
    
  73. 
    
  74.     k1 =
    
  75.       ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
    
  76.     k1 = (k1 << 15) | (k1 >>> 17);
    
  77.     k1 =
    
  78.       ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
    
  79. 
    
  80.     h1 ^= k1;
    
  81.     h1 = (h1 << 13) | (h1 >>> 19);
    
  82.     h1b =
    
  83.       ((h1 & 0xffff) * 5 + ((((h1 >>> 16) * 5) & 0xffff) << 16)) & 0xffffffff;
    
  84.     h1 = (h1b & 0xffff) + 0x6b64 + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16);
    
  85.   }
    
  86. 
    
  87.   k1 = 0;
    
  88. 
    
  89.   switch (remainder) {
    
  90.     case 3:
    
  91.       k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
    
  92.     case 2:
    
  93.       k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
    
  94.     case 1:
    
  95.       k1 ^= key.charCodeAt(i) & 0xff;
    
  96. 
    
  97.       k1 =
    
  98.         ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) &
    
  99.         0xffffffff;
    
  100.       k1 = (k1 << 15) | (k1 >>> 17);
    
  101.       k1 =
    
  102.         ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) &
    
  103.         0xffffffff;
    
  104.       h1 ^= k1;
    
  105.   }
    
  106. 
    
  107.   h1 ^= key.length;
    
  108. 
    
  109.   h1 ^= h1 >>> 16;
    
  110.   h1 =
    
  111.     ((h1 & 0xffff) * 0x85ebca6b +
    
  112.       ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) &
    
  113.     0xffffffff;
    
  114.   h1 ^= h1 >>> 13;
    
  115.   h1 =
    
  116.     ((h1 & 0xffff) * 0xc2b2ae35 +
    
  117.       ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) &
    
  118.     0xffffffff;
    
  119.   h1 ^= h1 >>> 16;
    
  120. 
    
  121.   return h1 >>> 0;
    
  122. }