Hashing in javascript

Benchmark created on


Description

Based upon https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript and https://gist.github.com/jlevy/c246006675becc446360a798e2b2d781 I sat up some simple tests on hashing for a list of strings resembling file names with a date variant.

Note that neither of these test actually compare the result, it just assumes for it to be a sensible value. The test do however convert any test result into a string.

Setup

const tests = [
 "some/folder/file.name.md20240307", 
 "some/folder/file.name878", 
 "someolder/le.name.md20240308", 
 "some/folder/file.md20240309", 
 "some/folder/file.name.md20940302", 
 "some/folder/file.nae.md20040302", 
 "somelder/file.name.md20140302", 
 "some/folder/file.name.md20440302", 
 "some/folder/file.name.md20340302", 
 "some/folder/file.name.md2040302", 
 "some/folder/file.name.024032", 
 "some/folder/file.name.md20210302", 
 "some/folder/file.name.md20200602", 
 "some/folder/file.name.md20210202", 
 "some/folder/file.name.md20210202", 
 "some/folder/file.name.md20240307", 
 "sme/folder/file.name878", 
 "smeolder/le.name.md20240308", 
 "sme/folder/file.md20240309", 
 "sme/folder/file.name.md20940302", 
 "sme/folder/file.nae.md20040302", 
 "smelder/file.name.md20140302", 
 "sme/folder/file.name.md20440302", 
 "sme/folder/file.name.md20340302", 
 "sme/folder/file.name.md2040302", 
 "sme/folder/file.name.024032", 
 "sme/folder/file.name.md20210302", 
 "ome/folder/file.name.md20200602", 
 "sme/folder/file.name.md20210202", 
 "soe/folder/file.name.md20210202", 
 ]
 const simpleHash = str => {
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    const char = str.charCodeAt(i);
    hash = (hash << 5) - hash + char;
  }
  // Convert to 32bit unsigned integer in base 36 and pad with "0" to ensure length is 7.
  return (hash >>> 0).toString(36).padStart(7, '0');
};

// cyrb53 (c) 2018 bryc (github.com/bryc). License: Public domain. Attribution appreciated.
// A fast and simple 64-bit (or 53-bit) string hash function with decent collision resistance.
// Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity.
// See https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript/52171480#52171480
// https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js
const cyrb53 = (str, seed = 0) => {
  let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
  for(let i = 0, ch; i < str.length; i++) {
    ch = str.charCodeAt(i);
    h1 = Math.imul(h1 ^ ch, 2654435761);
    h2 = Math.imul(h2 ^ ch, 1597334677);
  }
  h1  = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
  h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
  h2  = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
  h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
  // For a single 53-bit numeric return value we could return
  // 4294967296 * (2097151 & h2) + (h1 >>> 0);
  // but we instead return the full 64-bit value:
  return 4294967296 * (2097151 & h2) + (h1 >>> 0).toString(); // [h2>>>0, h1>>>0];
};

// cyrb64 - almost the same as above with 
// exception of the return statement

const cyrb64 = (str, seed = 0) => {
  let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
  for(let i = 0, ch; i < str.length; i++) {
    ch = str.charCodeAt(i);
    h1 = Math.imul(h1 ^ ch, 2654435761);
    h2 = Math.imul(h2 ^ ch, 1597334677);
  }
  h1  = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
  h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
  h2  = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
  h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
  // For a single 53-bit numeric return value we could return
  // 4294967296 * (2097151 & h2) + (h1 >>> 0);
  // but we instead return the full 64-bit value:
  return [h2>>>0, h1>>>0].toString();
};

const cyrb128 = (str) => {
    let h1 = 1779033703,
        h2 = 3144134277,
        h3 = 1013904242,
        h4 = 2773480762;
    for (let i = 0, k; i < str.length; i++) {
        k = str.charCodeAt(i);
        h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
        h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
        h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
        h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
    }
    h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);
    h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
    h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
    h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
    (h1 ^= h2 ^ h3 ^ h4), (h2 ^= h1), (h3 ^= h1), (h4 ^= h1);
    return [h1 >>> 0, h2 >>> 0, h3 >>> 0, h4 >>> 0].toString();
}

Test runner

Ready to run.

Testing in
TestOps/sec
simpleHash
for(str of tests) {
	simpleHash(str)
}
ready
cyrb53
for(str of tests) {
	cyrb53(str)
}
ready
cyrb64
for(str of tests) {
	cyrb64(str)
}
ready
cyrb128
for(str of tests) {
	cyrb128(str)
}
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.