| /** | |
| * Computes Effect hash values and defines the interface for objects that want | |
| * to provide their own hash implementation. Hashes are small numeric | |
| * fingerprints used by Effect data structures to bucket values quickly; they | |
| * are not cryptographic digests and they are not proof that two values are | |
| * equal. The module also includes helpers for primitive, structure, array, and | |
| * reference-based hashes, plus functions for combining and optimizing numeric | |
| * hash values. | |
| * | |
| * @since 2.0.0 | |
| */ | |
| import { dual } from "./Function.ts" | |
| import { byReferenceInstances, getAllObjectKeys } from "./internal/equal.ts" | |
| import { hasProperty } from "./Predicate.ts" | |
| /** | |
| * Defines the unique identifier used to identify objects that implement the Hash interface. | |
| * | |
| * **When to use** | |
| * | |
| * Use as the computed property key for the method that supplies a custom hash | |
| * value on a `Hash` implementor. | |
| * | |
| * @see {@link Hash} for the interface implemented with this symbol | |
| * @see {@link isHash} for checking whether a value implements `Hash` | |
| * @see {@link hash} for computing hash values | |
| * | |
| * @category symbols | |
| * @since 2.0.0 | |
| */ | |
| export const symbol = "~effect/interfaces/Hash" | |
| /** | |
| * A type that represents an object that can be hashed. | |
| * | |
| * **When to use** | |
| * | |
| * Use to let a custom type provide its own stable hash value. | |
| * | |
| * **Details** | |
| * | |
| * Objects implementing this interface provide a method to compute their hash value, | |
| * which is used for efficient comparison and storage operations. | |
| * | |
| * **Example** (Implementing Hash) | |
| * | |
| * ```ts | |
| * import { Hash } from "effect" | |
| * | |
| * class MyClass implements Hash.Hash { | |
| * constructor(private value: number) {} | |
| * | |
| * [Hash.symbol](): number { | |
| * return Hash.hash(this.value) | |
| * } | |
| * } | |
| * | |
| * const instance = new MyClass(42) | |
| * console.log(instance[Hash.symbol]()) // hash value of 42 | |
| * ``` | |
| * | |
| * @category models | |
| * @since 2.0.0 | |
| */ | |
| export interface Hash { | |
| [symbol](): number | |
| } | |
| /** | |
| * Computes a hash value for any given value. | |
| * | |
| * **When to use** | |
| * | |
| * Use to compute an Effect hash for primitives, collections, and hashable | |
| * objects. | |
| * | |
| * **Details** | |
| * | |
| * This function can hash primitives (numbers, strings, booleans, etc.) as well as | |
| * objects, arrays, and other complex data structures. It automatically handles | |
| * different types and provides a consistent hash value for equivalent inputs. | |
| * | |
| * **Gotchas** | |
| * | |
| * Objects being hashed must be treated as immutable after their first hash | |
| * computation. Hash results are cached, so mutating an object after hashing will | |
| * lead to stale cached values and broken hash-based operations. For mutable | |
| * objects, implement a custom `Hash` interface that hashes the object reference | |
| * rather than its content. | |
| * | |
| * **Example** (Hashing different values) | |
| * | |
| * ```ts | |
| * import { Hash } from "effect" | |
| * | |
| * // Hash primitive values | |
| * console.log(Hash.hash(42)) // numeric hash | |
| * console.log(Hash.hash("hello")) // string hash | |
| * console.log(Hash.hash(true)) // boolean hash | |
| * | |
| * // Hash objects and arrays | |
| * console.log(Hash.hash({ name: "John", age: 30 })) | |
| * console.log(Hash.hash([1, 2, 3])) | |
| * console.log(Hash.hash({ id: "user-1", roles: ["admin", "editor"] })) | |
| * ``` | |
| * | |
| * @category hashing | |
| * @since 2.0.0 | |
| */ | |
| export const hash: <A>(self: A) => number = <A>(self: A) => { | |
| switch (typeof self) { | |
| case "number": | |
| return number(self) | |
| case "bigint": | |
| return string(self.toString(10)) | |
| case "boolean": | |
| return string(String(self)) | |
| case "symbol": | |
| return string(String(self)) | |
| case "string": | |
| return string(self) | |
| case "undefined": | |
| return string("undefined") | |
| case "function": | |
| case "object": { | |
| if (self === null) { | |
| return string("null") | |
| } else if (self instanceof Date) { | |
| return string(self.toISOString()) | |
| } else if (self instanceof RegExp) { | |
| return string(self.toString()) | |
| } else { | |
| if (byReferenceInstances.has(self)) { | |
| return random(self) | |
| } | |
| if (hashCache.has(self)) { | |
| return hashCache.get(self)! | |
| } | |
| const h = withVisitedTracking(self, () => { | |
| if (isHash(self)) { | |
| return self[symbol]() | |
| } else if (typeof self === "function") { | |
| return random(self) | |
| } else if (Array.isArray(self) || ArrayBuffer.isView(self)) { | |
| return array(self as any) | |
| } else if (self instanceof Map) { | |
| return hashMap(self) | |
| } else if (self instanceof Set) { | |
| return hashSet(self) | |
| } | |
| return structure(self) | |
| }) | |
| hashCache.set(self, h) | |
| return h | |
| } | |
| } | |
| default: | |
| throw new Error( | |
| `BUG: unhandled typeof ${typeof self} - please report an issue at https://github.com/Effect-TS/effect/issues` | |
| ) | |
| } | |
| } | |
| /** | |
| * Generates a random hash value for an object and caches it. | |
| * | |
| * **When to use** | |
| * | |
| * Use to hash an object by reference identity instead of structural content. | |
| * | |
| * **Details** | |
| * | |
| * This function creates a random hash value for objects that don't have their own | |
| * hash implementation. The hash value is cached using a WeakMap, so the same object | |
| * will always return the same hash value during its lifetime. | |
| * | |
| * **Example** (Hashing objects by reference) | |
| * | |
| * ```ts | |
| * import { Hash } from "effect" | |
| * | |
| * const obj1 = { a: 1 } | |
| * const obj2 = { a: 1 } | |
| * | |
| * // Same object always returns the same hash | |
| * console.log(Hash.random(obj1) === Hash.random(obj1)) // true | |
| * | |
| * // Different objects get different hashes | |
| * console.log(Hash.random(obj1) === Hash.random(obj2)) // false | |
| * ``` | |
| * | |
| * @category hashing | |
| * @since 2.0.0 | |
| */ | |
| export const random: <A extends object>(self: A) => number = (self) => { | |
| if (!randomHashCache.has(self)) { | |
| randomHashCache.set(self, number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))) | |
| } | |
| return randomHashCache.get(self)! | |
| } | |
| /** | |
| * Combines two hash values into a single hash value. | |
| * | |
| * **When to use** | |
| * | |
| * Use to build a hash for a composite value by folding together hash values for | |
| * its parts. | |
| * | |
| * **Details** | |
| * | |
| * Supports both direct and pipeable usage. The implementation combines two | |
| * hash values with `(self * 53) ^ b`. | |
| * | |
| * **Example** (Combining hash values) | |
| * | |
| * ```ts | |
| * import { Hash, pipe } from "effect" | |
| * | |
| * // Can also be used with pipe | |
| * | |
| * const hash1 = Hash.hash("hello") | |
| * const hash2 = Hash.hash("world") | |
| * | |
| * // Combine two hash values | |
| * const combined = Hash.combine(hash2)(hash1) | |
| * console.log(combined) | |
| * const result = pipe(hash1, Hash.combine(hash2)) | |
| * ``` | |
| * | |
| * @see {@link hash} for computing hash values from arbitrary inputs | |
| * @see {@link structureKeys} for hashing selected object fields without manual combination | |
| * | |
| * @category hashing | |
| * @since 2.0.0 | |
| */ | |
| export const combine: { | |
| /** | |
| * Combines two hash values into a single hash value. | |
| * | |
| * **When to use** | |
| * | |
| * Use to build a hash for a composite value by folding together hash values for | |
| * its parts. | |
| * | |
| * **Details** | |
| * | |
| * Supports both direct and pipeable usage. The implementation combines two | |
| * hash values with `(self * 53) ^ b`. | |
| * | |
| * **Example** (Combining hash values) | |
| * | |
| * ```ts | |
| * import { Hash, pipe } from "effect" | |
| * | |
| * // Can also be used with pipe | |
| * | |
| * const hash1 = Hash.hash("hello") | |
| * const hash2 = Hash.hash("world") | |
| * | |
| * // Combine two hash values | |
| * const combined = Hash.combine(hash2)(hash1) | |
| * console.log(combined) | |
| * const result = pipe(hash1, Hash.combine(hash2)) | |
| * ``` | |
| * | |
| * @see {@link hash} for computing hash values from arbitrary inputs | |
| * @see {@link structureKeys} for hashing selected object fields without manual combination | |
| * | |
| * @category hashing | |
| * @since 2.0.0 | |
| */ | |
| (b: number): (self: number) => number | |
| /** | |
| * Combines two hash values into a single hash value. | |
| * | |
| * **When to use** | |
| * | |
| * Use to build a hash for a composite value by folding together hash values for | |
| * its parts. | |
| * | |
| * **Details** | |
| * | |
| * Supports both direct and pipeable usage. The implementation combines two | |
| * hash values with `(self * 53) ^ b`. | |
| * | |
| * **Example** (Combining hash values) | |
| * | |
| * ```ts | |
| * import { Hash, pipe } from "effect" | |
| * | |
| * // Can also be used with pipe | |
| * | |
| * const hash1 = Hash.hash("hello") | |
| * const hash2 = Hash.hash("world") | |
| * | |
| * // Combine two hash values | |
| * const combined = Hash.combine(hash2)(hash1) | |
| * console.log(combined) | |
| * const result = pipe(hash1, Hash.combine(hash2)) | |
| * ``` | |
| * | |
| * @see {@link hash} for computing hash values from arbitrary inputs | |
| * @see {@link structureKeys} for hashing selected object fields without manual combination | |
| * | |
| * @category hashing | |
| * @since 2.0.0 | |
| */ | |
| (self: number, b: number): number | |
| } = dual(2, (self: number, b: number): number => (self * 53) ^ b) | |
| /** | |
| * Applies bit manipulation techniques to optimize a hash value. | |
| * | |
| * **When to use** | |
| * | |
| * Use to improve the bit distribution of a raw numeric hash value. | |
| * | |
| * **Details** | |
| * | |
| * This function takes a hash value and applies bitwise operations to improve | |
| * the distribution of hash values, reducing the likelihood of collisions. | |
| * | |
| * **Example** (Optimizing a hash value) | |
| * | |
| * ```ts | |
| * import { Hash } from "effect" | |
| * | |
| * const rawHash = 1234567890 | |
| * const optimizedHash = Hash.optimize(rawHash) | |
| * console.log(optimizedHash) // optimized hash value | |
| * | |
| * // Often used internally by other hash functions | |
| * const stringHash = Hash.optimize(Hash.string("hello")) | |
| * ``` | |
| * | |
| * @category hashing | |
| * @since 2.0.0 | |
| */ | |
| export const optimize = (n: number): number => (n & 0xbfffffff) | ((n >>> 1) & 0x40000000) | |
| /** | |
| * Checks whether a value implements the Hash interface. | |
| * | |
| * **When to use** | |
| * | |
| * Use to detect whether an unknown value provides a custom hash implementation. | |
| * | |
| * **Details** | |
| * | |
| * This function determines whether a given value has the Hash symbol property, | |
| * indicating that it can provide its own hash value implementation. | |
| * | |
| * **Example** (Checking for Hash support) | |
| * | |
| * ```ts | |
| * import { Hash } from "effect" | |
| * | |
| * class MyHashable implements Hash.Hash { | |
| * [Hash.symbol]() { | |
| * return 42 | |
| * } | |
| * } | |
| * | |
| * const obj = new MyHashable() | |
| * console.log(Hash.isHash(obj)) // true | |
| * console.log(Hash.isHash({})) // false | |
| * console.log(Hash.isHash("string")) // false | |
| * ``` | |
| * | |
| * @category guards | |
| * @since 2.0.0 | |
| */ | |
| export const isHash = (u: unknown): u is Hash => hasProperty(u, symbol) | |
| /** | |
| * Computes a hash value for a number. | |
| * | |
| * **When to use** | |
| * | |
| * Use to hash a JavaScript number with Effect's numeric hash semantics. | |
| * | |
| * **Details** | |
| * | |
| * This function creates a hash value for numeric inputs, handling special cases | |
| * like NaN, Infinity, and -Infinity with distinct hash values. It uses bitwise operations to ensure good distribution | |
| * of hash values across different numeric inputs. | |
| * | |
| * **Example** (Hashing numbers) | |
| * | |
| * ```ts | |
| * import { Hash } from "effect" | |
| * | |
| * console.log(Hash.number(42)) // hash of 42 | |
| * console.log(Hash.number(3.14)) // hash of 3.14 | |
| * console.log(Hash.number(NaN)) // hash of "NaN" | |
| * console.log(Hash.number(Infinity)) // 0 (special case) | |
| * | |
| * // Same numbers produce the same hash | |
| * console.log(Hash.number(100) === Hash.number(100)) // true | |
| * ``` | |
| * | |
| * @category hashing | |
| * @since 2.0.0 | |
| */ | |
| export const number = (n: number) => { | |
| if (n !== n) { | |
| return string("NaN") | |
| } | |
| if (n === Infinity) { | |
| return string("Infinity") | |
| } | |
| if (n === -Infinity) { | |
| return string("-Infinity") | |
| } | |
| let h = n | 0 | |
| if (h !== n) { | |
| h ^= n * 0xffffffff | |
| } | |
| while (n > 0xffffffff) { | |
| h ^= n /= 0xffffffff | |
| } | |
| return optimize(h) | |
| } | |
| /** | |
| * Computes a hash value for a string using the djb2 algorithm. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need a string field to contribute to a custom structural hash | |
| * implementation. | |
| * | |
| * **Details** | |
| * | |
| * This function implements a variation of the djb2 hash algorithm, which is | |
| * known for its good distribution properties and speed. It processes each | |
| * character of the string to produce a consistent hash value. | |
| * | |
| * **Example** (Hashing strings) | |
| * | |
| * ```ts | |
| * import { Hash } from "effect" | |
| * | |
| * console.log(Hash.string("hello")) // hash of "hello" | |
| * console.log(Hash.string("world")) // hash of "world" | |
| * console.log(Hash.string("")) // hash of empty string | |
| * | |
| * // Same strings produce the same hash | |
| * console.log(Hash.string("test") === Hash.string("test")) // true | |
| * ``` | |
| * | |
| * @category hashing | |
| * @since 2.0.0 | |
| */ | |
| export const string = (str: string) => { | |
| let h = 5381, i = str.length | |
| while (i) { | |
| h = (h * 33) ^ str.charCodeAt(--i) | |
| } | |
| return optimize(h) | |
| } | |
| /** | |
| * Computes a hash value for an object using only the specified keys. | |
| * | |
| * **When to use** | |
| * | |
| * Use to hash an object by a selected set of property keys. | |
| * | |
| * **Details** | |
| * | |
| * This function allows you to hash an object by considering only specific keys, | |
| * which is useful when you want to create a hash based on a subset of an object's | |
| * properties. | |
| * | |
| * **Example** (Hashing selected object keys) | |
| * | |
| * ```ts | |
| * import { Hash } from "effect" | |
| * | |
| * const person = { name: "John", age: 30, city: "New York" } | |
| * | |
| * // Hash only specific keys | |
| * const hash1 = Hash.structureKeys(person, ["name", "age"]) | |
| * const hash2 = Hash.structureKeys(person, ["name", "city"]) | |
| * | |
| * console.log(hash1) // hash based on name and age | |
| * console.log(hash2) // hash based on name and city | |
| * | |
| * // Same keys produce the same hash | |
| * const person2 = { name: "John", age: 30, city: "Boston" } | |
| * const hash3 = Hash.structureKeys(person2, ["name", "age"]) | |
| * console.log(hash1 === hash3) // true | |
| * ``` | |
| * | |
| * @category hashing | |
| * @since 2.0.0 | |
| */ | |
| export const structureKeys = (o: object, keys: Iterable<PropertyKey>) => { | |
| let h = 12289 | |
| for (const key of keys) { | |
| h ^= combine(hash(key), hash((o as any)[key])) | |
| } | |
| return optimize(h) | |
| } | |
| /** | |
| * Computes a structural hash for an object using Effect's object key collection. | |
| * | |
| * **When to use** | |
| * | |
| * Use to hash an object from all structural keys collected by Effect. | |
| * | |
| * **Details** | |
| * | |
| * The hash is based on the object's structural keys and their values, including | |
| * symbol keys and relevant prototype keys for non-plain objects. | |
| * | |
| * **Example** (Hashing object structures) | |
| * | |
| * ```ts | |
| * import { Hash } from "effect" | |
| * | |
| * const obj1 = { name: "John", age: 30 } | |
| * const obj2 = { name: "Jane", age: 25 } | |
| * const obj3 = { name: "John", age: 30 } | |
| * | |
| * console.log(Hash.structure(obj1)) // hash of obj1 | |
| * console.log(Hash.structure(obj2)) // different hash | |
| * console.log(Hash.structure(obj3)) // same as obj1 | |
| * | |
| * // Objects with same properties produce same hash | |
| * console.log(Hash.structure(obj1) === Hash.structure(obj3)) // true | |
| * ``` | |
| * | |
| * @category hashing | |
| * @since 2.0.0 | |
| */ | |
| export const structure = <A extends object>(o: A) => structureKeys(o, getAllObjectKeys(o)) | |
| const iterableWith = (seed: number, f: (el: any) => number) => (iter: Iterable<any>) => { | |
| let h = seed | |
| for (const element of iter) { | |
| h ^= f(element) | |
| } | |
| return optimize(h) | |
| } | |
| /** | |
| * Computes a hash value for an iterable by hashing all of its elements. | |
| * | |
| * **When to use** | |
| * | |
| * Use to hash the values yielded by an iterable with Effect hash semantics. | |
| * | |
| * **Details** | |
| * | |
| * The implementation folds element hashes from the seed `6151` with XOR and | |
| * then optimizes the final hash. | |
| * | |
| * **Gotchas** | |
| * | |
| * A hash is not an equality proof. Because this implementation uses XOR, | |
| * reordered inputs can produce the same hash. | |
| * | |
| * **Example** (Hashing arrays) | |
| * | |
| * ```ts | |
| * import { Hash } from "effect" | |
| * | |
| * const arr1 = [1, 2, 3] | |
| * const arr2 = [1, 2, 3] | |
| * const arr3 = [3, 2, 1] | |
| * | |
| * console.log(Hash.array(arr1)) // hash of [1, 2, 3] | |
| * console.log(Hash.array(arr2)) // same hash as arr1 | |
| * console.log(Hash.array(arr3)) // may match reordered inputs | |
| * | |
| * console.log(Hash.array(arr1) === Hash.array(arr2)) // true | |
| * console.log(Hash.array(arr1) === Hash.array(arr3)) // true | |
| * ``` | |
| * | |
| * @see {@link hash} for the general-purpose hash dispatcher | |
| * | |
| * @category hashing | |
| * @since 2.0.0 | |
| */ | |
| export const array: <A>(arr: Iterable<A>) => number = iterableWith(6151, hash) | |
| const hashMap: <K, V>(map: Iterable<readonly [K, V]>) => number = iterableWith( | |
| string("Map"), | |
| ([k, v]) => combine(hash(k), hash(v)) | |
| ) | |
| const hashSet: <A>(set: Iterable<A>) => number = iterableWith(string("Set"), hash) | |
| const randomHashCache = new WeakMap<any, number>() | |
| const hashCache = new WeakMap<any, number>() | |
| const visitedObjects = new WeakSet<object>() | |
| function withVisitedTracking<T>(obj: object, fn: () => T): T { | |
| if (visitedObjects.has(obj)) { | |
| return string("[Circular]") as T | |
| } | |
| visitedObjects.add(obj) | |
| const result = fn() | |
| visitedObjects.delete(obj) | |
| return result | |
| } | |
Xet Storage Details
- Size:
- 16.9 kB
- Xet hash:
- 3a87be0217f6593e7508b111a14ef2a0df47200ebc6026e2af6a7e4b783a3818
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.