@sinclair/typebox
Version:
Json Schema Type Builder with Static Type Resolution for TypeScript
153 lines (151 loc) • 5.12 kB
JavaScript
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('2') ** BigInt('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;
}
;