153 lines
5.0 KiB
Plaintext
153 lines
5.0 KiB
Plaintext
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.ValueHashError = void 0;
|
|
exports.Hash = Hash;
|
|
const index_1 = require("../guard/index");
|
|
const index_2 = require("../../type/error/index");
|
|
// ------------------------------------------------------------------
|
|
// Errors
|
|
// ------------------------------------------------------------------
|
|
class ValueHashError extends index_2.TypeBoxError {
|
|
constructor(value) {
|
|
super(`Unable to hash value`);
|
|
this.value = value;
|
|
}
|
|
}
|
|
exports.ValueHashError = ValueHashError;
|
|
// ------------------------------------------------------------------
|
|
// ByteMarker
|
|
// ------------------------------------------------------------------
|
|
var ByteMarker;
|
|
(function (ByteMarker) {
|
|
ByteMarker[ByteMarker["Undefined"] = 0] = "Undefined";
|
|
ByteMarker[ByteMarker["Null"] = 1] = "Null";
|
|
ByteMarker[ByteMarker["Boolean"] = 2] = "Boolean";
|
|
ByteMarker[ByteMarker["Number"] = 3] = "Number";
|
|
ByteMarker[ByteMarker["String"] = 4] = "String";
|
|
ByteMarker[ByteMarker["Object"] = 5] = "Object";
|
|
ByteMarker[ByteMarker["Array"] = 6] = "Array";
|
|
ByteMarker[ByteMarker["Date"] = 7] = "Date";
|
|
ByteMarker[ByteMarker["Uint8Array"] = 8] = "Uint8Array";
|
|
ByteMarker[ByteMarker["Symbol"] = 9] = "Symbol";
|
|
ByteMarker[ByteMarker["BigInt"] = 10] = "BigInt";
|
|
})(ByteMarker || (ByteMarker = {}));
|
|
// ------------------------------------------------------------------
|
|
// State
|
|
// ------------------------------------------------------------------
|
|
let Accumulator = BigInt('14695981039346656037');
|
|
const [Prime, Size] = [BigInt('1099511628211'), BigInt('18446744073709551616' /* 2 ^ 64 */)];
|
|
const Bytes = Array.from({ length: 256 }).map((_, i) => BigInt(i));
|
|
const F64 = new Float64Array(1);
|
|
const F64In = new DataView(F64.buffer);
|
|
const F64Out = new Uint8Array(F64.buffer);
|
|
// ------------------------------------------------------------------
|
|
// NumberToBytes
|
|
// ------------------------------------------------------------------
|
|
function* NumberToBytes(value) {
|
|
const byteCount = value === 0 ? 1 : Math.ceil(Math.floor(Math.log2(value) + 1) / 8);
|
|
for (let i = 0; i < byteCount; i++) {
|
|
yield (value >> (8 * (byteCount - 1 - i))) & 0xff;
|
|
}
|
|
}
|
|
// ------------------------------------------------------------------
|
|
// Hashing Functions
|
|
// ------------------------------------------------------------------
|
|
function ArrayType(value) {
|
|
FNV1A64(ByteMarker.Array);
|
|
for (const item of value) {
|
|
Visit(item);
|
|
}
|
|
}
|
|
function BooleanType(value) {
|
|
FNV1A64(ByteMarker.Boolean);
|
|
FNV1A64(value ? 1 : 0);
|
|
}
|
|
function BigIntType(value) {
|
|
FNV1A64(ByteMarker.BigInt);
|
|
F64In.setBigInt64(0, value);
|
|
for (const byte of F64Out) {
|
|
FNV1A64(byte);
|
|
}
|
|
}
|
|
function DateType(value) {
|
|
FNV1A64(ByteMarker.Date);
|
|
Visit(value.getTime());
|
|
}
|
|
function NullType(value) {
|
|
FNV1A64(ByteMarker.Null);
|
|
}
|
|
function NumberType(value) {
|
|
FNV1A64(ByteMarker.Number);
|
|
F64In.setFloat64(0, value);
|
|
for (const byte of F64Out) {
|
|
FNV1A64(byte);
|
|
}
|
|
}
|
|
function ObjectType(value) {
|
|
FNV1A64(ByteMarker.Object);
|
|
for (const key of globalThis.Object.getOwnPropertyNames(value).sort()) {
|
|
Visit(key);
|
|
Visit(value[key]);
|
|
}
|
|
}
|
|
function StringType(value) {
|
|
FNV1A64(ByteMarker.String);
|
|
for (let i = 0; i < value.length; i++) {
|
|
for (const byte of NumberToBytes(value.charCodeAt(i))) {
|
|
FNV1A64(byte);
|
|
}
|
|
}
|
|
}
|
|
function SymbolType(value) {
|
|
FNV1A64(ByteMarker.Symbol);
|
|
Visit(value.description);
|
|
}
|
|
function Uint8ArrayType(value) {
|
|
FNV1A64(ByteMarker.Uint8Array);
|
|
for (let i = 0; i < value.length; i++) {
|
|
FNV1A64(value[i]);
|
|
}
|
|
}
|
|
function UndefinedType(value) {
|
|
return FNV1A64(ByteMarker.Undefined);
|
|
}
|
|
function Visit(value) {
|
|
if ((0, index_1.IsArray)(value))
|
|
return ArrayType(value);
|
|
if ((0, index_1.IsBoolean)(value))
|
|
return BooleanType(value);
|
|
if ((0, index_1.IsBigInt)(value))
|
|
return BigIntType(value);
|
|
if ((0, index_1.IsDate)(value))
|
|
return DateType(value);
|
|
if ((0, index_1.IsNull)(value))
|
|
return NullType(value);
|
|
if ((0, index_1.IsNumber)(value))
|
|
return NumberType(value);
|
|
if ((0, index_1.IsObject)(value))
|
|
return ObjectType(value);
|
|
if ((0, index_1.IsString)(value))
|
|
return StringType(value);
|
|
if ((0, index_1.IsSymbol)(value))
|
|
return SymbolType(value);
|
|
if ((0, index_1.IsUint8Array)(value))
|
|
return Uint8ArrayType(value);
|
|
if ((0, index_1.IsUndefined)(value))
|
|
return UndefinedType(value);
|
|
throw new ValueHashError(value);
|
|
}
|
|
function FNV1A64(byte) {
|
|
Accumulator = Accumulator ^ Bytes[byte];
|
|
Accumulator = (Accumulator * Prime) % Size;
|
|
}
|
|
// ------------------------------------------------------------------
|
|
// Hash
|
|
// ------------------------------------------------------------------
|
|
/** Creates a FNV1A-64 non cryptographic hash of the given value */
|
|
function Hash(value) {
|
|
Accumulator = BigInt('14695981039346656037');
|
|
Visit(value);
|
|
return Accumulator;
|
|
}
|