Skip to content

Instantly share code, notes, and snippets.

@nberlette
Created May 21, 2026 07:59
Show Gist options
  • Select an option

  • Save nberlette/cfa35def3a7990e87a4012435bbab854 to your computer and use it in GitHub Desktop.

Select an option

Save nberlette/cfa35def3a7990e87a4012435bbab854 to your computer and use it in GitHub Desktop.
murmurhash3
export type DigestType = 'raw' | 'alphanumeric';
/**
* Thin wrapper around `MurmurHash3`
*/
export class Hash extends MurmurHash3 {
constructor(key?: string, seed?: number) {
super(key, seed);
}
/**
* update the hash content with the given `data`.
*/
update(data: string | Hash): Hash {
this.hash(data instanceof Hash ? data.digest() : data);
return this;
}
/**
* compute the digest of all data passed to the hash. The digest can either
* be:
* - `'raw'`, returning a 32-bit positive integer (the raw result of the
* mumrmurHash algorithm)
* - `'alphanumeric'` (default), returning an alphanumeric string
* ([a-z0-9])
*/
digest(): string;
digest(type: 'raw'): number;
digest(type: 'alphanumeric'): string;
digest(type?: DigestType): string | number {
switch (type) {
case 'raw':
return this.result();
case undefined: /* fall through */
case 'alphanumeric':
default:
return this.result().toString(36);
}
}
static from(data: string | Hash, type?: DigestType): string | number {
return new Hash().update(data).digest(type);
}
}
/**
* @author <a href="mailto:jensyt@gmail.com">Jens Taylor</a>
* @see http://github.com/homebrewing/brauhaus-diff
* @author <a href="mailto:gary.court@gmail.com">Gary Court</a>
* @see http://github.com/garycourt/murmurhash-js
* @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a>
* @see http://sites.google.com/site/murmurhash/
* @author <a href="https://github.com/jensyt">Jens Taylor</a>
* @see https://github.com/jensyt/imurmurhash-js
*/
/**
* JS Implementation of incremental MurmurHash3 (r150) (as of May 10, 2013)
*/
class MurmurHash3 {
private len!: number;
private rem!: number;
private h1!: number;
private k1!: number;
/**
* Create a MurmurHash3 object for incremental hashing.
* @param key key A UTF-16 or ASCII string
* @param seed seed An optional positive integer
*/
constructor(key?: string, seed?: number) {
this.reset(seed);
if (typeof key === "string" && key.length > 0) {
this.hash(key);
}
}
/**
* Incrementally add a string to this hash
* @param key key A UTF-16 or ASCII string
*/
hash(key: string) {
let h1: number;
let top: number;
let len = key.length;
this.len += len;
let k1 = this.k1;
let i = 0;
switch (this.rem) {
case 0:
k1 ^= len > i ? key.charCodeAt(i++) & 0xffff : 0;
case 1:
k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) << 8 : 0;
case 2:
k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) << 16 : 0;
case 3:
k1 ^= len > i ? (key.charCodeAt(i) & 0xff) << 24 : 0;
k1 ^= len > i ? (key.charCodeAt(i++) & 0xff00) >> 8 : 0;
}
this.rem = (len + this.rem) & 3; // & 3 is same as % 4
len -= this.rem;
if (len > 0) {
h1 = this.h1;
while (1) {
k1 = (k1 * 0x2d51 + (k1 & 0xffff) * 0xcc9e0000) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = (k1 * 0x3593 + (k1 & 0xffff) * 0x1b870000) & 0xffffffff;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19);
h1 = (h1 * 5 + 0xe6546b64) & 0xffffffff;
if (i >= len) {
break;
}
k1 = (key.charCodeAt(i++) & 0xffff) ^
((key.charCodeAt(i++) & 0xffff) << 8) ^
((key.charCodeAt(i++) & 0xffff) << 16);
top = key.charCodeAt(i++);
k1 ^= ((top & 0xff) << 24) ^ ((top & 0xff00) >> 8);
}
k1 = 0;
switch (this.rem) {
case 3:
k1 ^= (key.charCodeAt(i + 2) & 0xffff) << 16;
case 2:
k1 ^= (key.charCodeAt(i + 1) & 0xffff) << 8;
case 1:
k1 ^= key.charCodeAt(i) & 0xffff;
}
this.h1 = h1;
}
this.k1 = k1;
}
/**
* Get the result of this hash
*/
result(): number {
let k1 = this.k1;
let h1 = this.h1;
if (k1 > 0) {
k1 = (k1 * 0x2d51 + (k1 & 0xffff) * 0xcc9e0000) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = (k1 * 0x3593 + (k1 & 0xffff) * 0x1b870000) & 0xffffffff;
h1 ^= k1;
}
h1 ^= this.len;
h1 ^= h1 >>> 16;
h1 = (h1 * 0xca6b + (h1 & 0xffff) * 0x85eb0000) & 0xffffffff;
h1 ^= h1 >>> 13;
h1 = (h1 * 0xae35 + (h1 & 0xffff) * 0xc2b20000) & 0xffffffff;
h1 ^= h1 >>> 16;
return h1 >>> 0;
}
/**
* Reset the hash object for reuse
* @param seed seed An optional positive integer
*/
reset(seed?: number) {
this.h1 = typeof seed === "number" ? seed : 0;
this.rem = this.k1 = this.len = 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment