Created
August 26, 2020 06:42
-
-
Save leidegre/00c9d3ca58c91b253851e5c19c487d58 to your computer and use it in GitHub Desktop.
Hashing and strict value equality for JavaScript primitives
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
const xxh_1 = require("./xxh"); | |
// Hash randomization | |
const SEED_ARRAY = (1000000000 * Math.random()) | 0; | |
const SEED_BIG = (1000000000 * Math.random()) | 0; | |
const SEED_NUM = (1000000000 * Math.random()) | 0; | |
const SEED_STR = (1000000000 * Math.random()) | 0; | |
const SEED_KEY = (1000000000 * Math.random()) | 0; | |
const SEED_DATE = (1000000000 * Math.random()) | 0; | |
const SEED_BOOL = (1000000000 * Math.random()) | 0; | |
const SEED_NULL = (1000000000 * Math.random()) | 0; | |
const XXH_FALSE = xxh_1.xxh_final(xxh_1.xxh_update(xxh_1.xxh_init(SEED_BOOL), 0)); | |
const XXH_TRUE = xxh_1.xxh_final(xxh_1.xxh_update(xxh_1.xxh_init(SEED_BOOL), 1)); | |
const XXH_NULL = xxh_1.xxh_final(xxh_1.xxh_update(xxh_1.xxh_init(SEED_NULL), 0)); | |
const XXH_PRIV_SYM_NO_DESC = xxh_1.xxh_final(xxh_1.xxh_update(xxh_1.xxh_init(SEED_KEY), 0)); | |
// union | |
const u = new ArrayBuffer(8); | |
const i32 = new Int32Array(u); | |
const i64 = new BigInt64Array(u); | |
const f64 = new Float64Array(u); | |
/** | |
* Compute the hash code of a JavaScript primitive value. Note that objects are not values. Arrays are permitted in the case they are used as tuples. | |
* | |
* Improper use will throw `TypeError`. | |
* | |
* @param v any JavaScript primitive `bigint`, `boolean`, `number`, `Date`, `Array`, `null`, `string` or `symbol` | |
*/ | |
function hashCode(v) { | |
switch (typeof v) { | |
case "bigint": { | |
i64[0] = v; // truncate | |
let h = xxh_1.xxh_init(SEED_BIG); | |
h = xxh_1.xxh_update(h, i32[0]); | |
h = xxh_1.xxh_update(h, i32[1]); | |
return xxh_1.xxh_final(h); | |
} | |
case "boolean": | |
return v ? XXH_TRUE : XXH_FALSE; | |
case "number": { | |
f64[0] = v; | |
let h = xxh_1.xxh_init(SEED_NUM); | |
h = xxh_1.xxh_update(h, i32[0]); | |
h = xxh_1.xxh_update(h, i32[1]); | |
return xxh_1.xxh_final(h); | |
} | |
case "object": { | |
if (v instanceof Date) { | |
return xxh_1.xxh_final(xxh_1.xxh_update(xxh_1.xxh_init(SEED_DATE), +v)); | |
} | |
if (Array.isArray(v)) { | |
let h = xxh_1.xxh_init((SEED_ARRAY + (v.length << 2)) | 0); | |
for (let i = 0; i < v.length; i++) { | |
h = xxh_1.xxh_update(h, hashCode(v[i])); | |
} | |
return xxh_1.xxh_final(h); | |
} | |
if (v === null) { | |
return XXH_NULL; | |
} | |
// the main reason for this is testing hash collisions, | |
// it's not recommended that you write your own `hashCode()` function | |
if (_hashCodeFunction(v)) { | |
return v.hashCode(); | |
} | |
throw new TypeError(`[object ${v.constructor.name}] is not a supported primitive`); | |
} | |
case "string": { | |
return xxh_1.xxh_str(v, SEED_STR); | |
} | |
case "symbol": { | |
const description = v.description; | |
if (description !== undefined) { | |
return xxh_1.xxh_str(description, SEED_KEY); | |
} | |
return XXH_PRIV_SYM_NO_DESC; | |
} | |
} | |
throw new TypeError(`value with type ${typeof v} is not a supported primitive`); | |
} | |
exports.hashCode = hashCode; | |
/** test whether object has custom `hashCode()` function */ | |
function _hashCodeFunction(obj) { | |
return typeof obj.hashCode === "function"; | |
} | |
function equals(a, b) { | |
switch (typeof a) { | |
case "object": { | |
if (a instanceof Date) { | |
if (b instanceof Date) { | |
return +a === +b; | |
} | |
return false; | |
} | |
if (Array.isArray(a)) { | |
if (Array.isArray(b)) { | |
if (a.length === b.length) { | |
for (let i = 0; i < a.length; i++) { | |
if (!equals(a[i], b[i])) { | |
return false; | |
} | |
} | |
return true; | |
} | |
} | |
return false; | |
} | |
break; | |
} | |
} | |
return a === b; | |
} | |
exports.equals = equals; | |
//# sourceMappingURL=eq.js.map |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment