D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
home
/
etb1lp46s9ed
/
washeet.softurecs.com
/
node_modules
/
hashery
/
Filename :
README.md
back
Copy
<img src="./site/logo.svg" /> # hashery Browser / Nodejs Compatible Object Hashing [](https://github.com/jaredwray/hashery/actions/workflows/tests.yml) [](https://codecov.io/gh/jaredwray/hashery) [](https://github.com/jaredwray/hashery/blob/master/LICENSE) [](https://npmjs.com/package/hashery) [](https://www.jsdelivr.com/package/npm/hashery) [](https://npmjs.com/package/hashery) # Features - **Simple and Easy Object Hashing** - Object hashing based on multiple algorithms. - **Browser and Node.js Compatible** - Built using `WebCrypto` API for both environments - **Multiple Hash Algorithms** - Supports SHA-256, SHA-384, SHA-512 (WebCrypto), plus DJB2, FNV1, Murmer, and CRC32 - **Synchronous & Asynchronous** - Both sync and async methods for flexible integration - **Custom Serialization** - Easily replace JSON `parse` and `stringify` with custom functions - **Deterministic Hashing** - Generate consistent hashes for the same input - **Hash to Number** - Convert hashes to deterministic numbers within a specified range. Great for slot management - **Provider System** - Extensible hash provider architecture for custom algorithms - **Fuzzy Provider Matching** - Case-insensitive and dash-tolerant algorithm name matching - **Hooks Support** - Extends Hookified for event-based functionality - **Maintained on a Regular Basis** - Active maintenance and updates # Table of Contents - [Features](#features) - [Installation](#installation) - [Usage](#usage) - [Basic Hashing](#basic-hashing) - [Synchronous Hashing](#synchronous-hashing) - [Using Different Hash Algorithms](#using-different-hash-algorithms) - [Setting a Default Algorithm](#setting-a-default-algorithm) - [Truncating Hash Output](#truncating-hash-output) - [Hash to Number (Great for Slot Management)](#hash-to-number-great-for-slot-management) - [Hash to Number Synchronous](#hash-to-number-synchronous) - [Browser Usage](#browser-usage) - [Hooks](#hooks) - [Caching](#caching) - [Web Crypto](#web-crypto) - [DJB2 Hashing](#djb2-hashing) - [FNV1 Hashing](#fnv1-hashing) - [CRC Hashing](#crc-hashing) - [API - Properties](#api---properties) - [parse](#parse) - [stringify](#stringify) - [providers](#providers) - [names](#names) - [defaultAlgorithm](#defaultalgorithm) - [defaultAlgorithmSync](#defaultalgorithmsync) - [API - Functions](#api---functions) - [toHash(data, options?)](#toHashdata-options) - [toHashSync(data, options?)](#toHashsyncdata-options) - [toNumber(data, options?)](#tonumberdata-options) - [toNumberSync(data, options?)](#tonumbersyncdata-options) - [loadProviders(providers?, options?)](#loadprovidersproviders-options) - [Benchmarks](#benchmarks) - [Code of Conduct and Contributing](#code-of-conduct-and-contributing) - [License and Copyright](#license-and-copyright) # Installation ```bash npm install hashery ``` # Usage ## Basic Hashing ```typescript import { Hashery } from 'hashery'; const hashery = new Hashery(); // Hash an object (defaults to SHA-256) const hash = await hashery.toHash({ name: 'John', age: 30 }); console.log(hash); // SHA-256 hash string // Hash a string const stringHash = await hashery.toHash('hello world'); // Hash any value (numbers, arrays, etc.) const numberHash = await hashery.toHash(42); const arrayHash = await hashery.toHash([1, 2, 3, 4, 5]); ``` ## Synchronous Hashing For performance-critical applications or when you need to avoid async/await, use the synchronous hashing methods. These work with non-cryptographic hash algorithms (djb2, fnv1, murmer, crc32) and are significantly faster than WebCrypto methods. ```typescript import { Hashery } from 'hashery'; const hashery = new Hashery(); // Synchronous hash (defaults to djb2) const hash = hashery.toHashSync({ name: 'John', age: 30 }); console.log(hash); // djb2 hash string (8 hex characters) // Sync with specific algorithm const fnv1Hash = hashery.toHashSync({ data: 'example' }, { algorithm: 'fnv1' }); const murmerHash = hashery.toHashSync({ data: 'example' }, { algorithm: 'murmer' }); const crcHash = hashery.toHashSync({ data: 'example' }, { algorithm: 'crc32' }); // Note: WebCrypto algorithms (SHA-256, SHA-384, SHA-512) are NOT supported in sync mode // This will throw an error: // hashery.toHashSync({ data: 'example' }, { algorithm: 'SHA-256' }); // ❌ Error! ``` ## Using Different Hash Algorithms ```typescript import { Hashery } from 'hashery'; const hashery = new Hashery(); // Use SHA-384 const hash384 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-384' }); // Use SHA-512 const hash512 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-512' }); // Use non-crypto hash algorithms const fastHash = await hashery.toHash({ data: 'example' }, { algorithm: 'djb2' }); ``` ## Setting a Default Algorithm You can set a default algorithm for all hash operations via constructor or property: ```typescript import { Hashery } from 'hashery'; // Set default algorithm via constructor const hashery = new Hashery({ defaultAlgorithm: 'SHA-512' }); // Now all hashes use SHA-512 by default const hash1 = await hashery.toHash({ data: 'example' }); // Uses SHA-512 console.log(hash1.length); // 128 (SHA-512 produces 128 hex characters) // You can still override it per call const hash2 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-256' }); console.log(hash2.length); // 64 (SHA-256 produces 64 hex characters) // Change default algorithm at runtime hashery.defaultAlgorithm = 'djb2'; const hash3 = await hashery.toHash({ data: 'example' }); // Uses djb2 ``` ## Truncating Hash Output You can limit the length of the hash output using the `maxLength` option: ```typescript import { Hashery } from 'hashery'; const hashery = new Hashery(); // Get a shorter hash (16 characters instead of 64) const shortHash = await hashery.toHash( { data: 'example' }, { algorithm: 'SHA-256', maxLength: 16 } ); console.log(shortHash); // "3f79bb7b435b0518" (16 chars) // Full hash for comparison const fullHash = await hashery.toHash({ data: 'example' }); console.log(fullHash); // "3f79bb7b435b05181e4ccf0d4e8..." (64 chars) ``` ## Hash to Number (Great for Slot Management) ```typescript import { Hashery } from 'hashery'; const hashery = new Hashery(); // Convert hash to a number within a range const slot = await hashery.toNumber({ userId: 123 }, { min: 0, max: 100 }); console.log(slot); // Deterministic number between 0-100 // Use for consistent slot assignment const userSlot = await hashery.toNumber({ userId: 'user@example.com' }, { min: 0, max: 9 }); // Same user will always get the same slot number ``` ## Hash to Number Synchronous Generate deterministic numbers synchronously for high-performance scenarios. Perfect for A/B testing, sharding, and load balancing without async overhead. ```typescript import { Hashery } from 'hashery'; const hashery = new Hashery(); // Synchronous number generation (defaults to djb2) const slot = hashery.toNumberSync({ userId: 123 }, { min: 0, max: 100 }); console.log(slot); // Deterministic number between 0-100 // A/B testing without async/await const variant = hashery.toNumberSync({ userId: 'user123' }, { min: 0, max: 1 }); console.log(variant === 0 ? 'Group A' : 'Group B'); // Load balancing across servers const serverIndex = hashery.toNumberSync( { requestId: 'req_abc123' }, { min: 0, max: 9, algorithm: 'fnv1' } // 10 servers ); // Sharding assignment const shardId = hashery.toNumberSync( { customerId: 'cust_xyz' }, { min: 0, max: 15, algorithm: 'murmer' } // 16 shards ); // Set default sync algorithm for all sync operations const hashery2 = new Hashery({ defaultAlgorithmSync: 'fnv1' }); const num = hashery2.toNumberSync({ data: 'test' }); // Uses fnv1 by default ``` ## Browser Usage Hashery works seamlessly in the browser using the Web Crypto API. You can include it via CDN or bundle it with your application. ### Using via CDN (jsDelivr) ```html <!DOCTYPE html> <html> <head> <title>Hashery Browser Example</title> </head> <body> <script type="module"> import { Hashery } from 'https://cdn.jsdelivr.net/npm/hashery@latest/dist/browser/index.js'; const hashery = new Hashery(); // Hash data in the browser const hash = await hashery.toHash({ page: 'home', userId: 123 }); console.log('Hash:', hash); // Generate slot numbers for A/B testing const variant = await hashery.toNumber({ userId: 'user123' }, { min: 0, max: 1 }); console.log('A/B Test Variant:', variant === 0 ? 'A' : 'B'); </script> </body> </html> ``` # Hooks Hashery extends [Hookified](https://github.com/jaredwray/hookified) to provide event-based functionality through hooks. Hooks allow you to intercept and modify behavior during the hashing process. ## Available Hooks ### Asynchronous Method Hooks #### `before:toHash` Fired before hashing occurs. This hook receives a context object containing: - `data` - The data to be hashed (can be modified) - `algorithm` - The hash algorithm to use (can be modified) - `maxLength` - Optional maximum length for the hash output #### `after:toHash` Fired after hashing completes. This hook receives a result object containing: - `hash` - The generated hash (can be modified) - `data` - The data that was hashed - `algorithm` - The algorithm that was used ### Synchronous Method Hooks #### `before:toHashSync` Fired before synchronous hashing occurs. This hook receives a context object containing: - `data` - The data to be hashed (can be modified) - `algorithm` - The hash algorithm to use (can be modified) - `maxLength` - Optional maximum length for the hash output **Note:** This hook fires asynchronously (non-blocking) to maintain the synchronous nature of `toHashSync()`. Hook execution happens in the background and won't delay the method's return. #### `after:toHashSync` Fired after synchronous hashing completes. This hook receives a result object containing: - `hash` - The generated hash (can be modified) - `data` - The data that was hashed - `algorithm` - The algorithm that was used **Note:** This hook fires asynchronously (non-blocking) to maintain the synchronous nature of `toHashSync()`. Hook execution happens in the background. ## Basic Hook Usage ```typescript import { Hashery } from 'hashery'; const hashery = new Hashery(); // Listen to before:toHash hook hashery.onHook('before:toHash', async (context) => { console.log('About to hash:', context.data); console.log('Using algorithm:', context.algorithm); }); // Listen to after:toHash hook hashery.onHook('after:toHash', async (result) => { console.log('Hash generated:', result.hash); console.log('Original data:', result.data); }); await hashery.toHash({ name: 'John', age: 30 }); ``` ## Modifying Data with Hooks You can modify the data before it's hashed: ```typescript const hashery = new Hashery(); // Add a timestamp to all hashed data hashery.onHook('before:toHash', async (context) => { context.data = { original: context.data, timestamp: new Date().toISOString() }; }); const hash = await hashery.toHash({ userId: 123 }); // Data will be hashed with timestamp included ``` ## Modifying Algorithms with Hooks You can force a specific algorithm regardless of what's requested: ```typescript const hashery = new Hashery(); // Force all hashes to use SHA-512 hashery.onHook('before:toHash', async (context) => { context.algorithm = 'SHA-512'; }); // Even though we request SHA-256, it will use SHA-512 const hash = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-256' }); console.log(hash.length); // 128 (SHA-512 hash length) ``` ## Modifying Hash Results You can transform the hash after it's generated: ```typescript const hashery = new Hashery(); // Convert all hashes to uppercase hashery.onHook('after:toHash', async (result) => { result.hash = result.hash.toUpperCase(); }); const hash = await hashery.toHash({ data: 'example' }); console.log(hash); // Hash will be in uppercase ``` ## Implementing Caching with Hooks Use hooks to implement a caching layer: ```typescript const hashery = new Hashery(); const cache = new Map<string, string>(); // Store hashes in cache after generation hashery.onHook('after:toHash', async (result) => { const cacheKey = `${result.algorithm}:${JSON.stringify(result.data)}`; cache.set(cacheKey, result.hash); }); // Later you can check the cache before hashing // (Note: You would need to implement cache lookup logic in your application) ``` ## Logging and Debugging Hooks are perfect for logging and debugging: ```typescript const hashery = new Hashery(); hashery.onHook('before:toHash', async (context) => { console.log(`[DEBUG] Hashing data with ${context.algorithm}:`, context.data); }); hashery.onHook('after:toHash', async (result) => { console.log(`[DEBUG] Hash generated: ${result.hash.substring(0, 8)}...`); }); await hashery.toHash({ userId: 'user123' }); ``` ## Multiple Hooks You can register multiple hooks, and they will execute in the order they were registered: ```typescript const hashery = new Hashery(); hashery.onHook('before:toHash', async (context) => { console.log('First hook'); context.data = { step: 1, original: context.data }; }); hashery.onHook('before:toHash', async (context) => { console.log('Second hook'); context.data = { step: 2, previous: context.data }; }); await hashery.toHash({ name: 'test' }); // Output: "First hook" then "Second hook" // Data will be wrapped twice ``` ## Synchronous Method Hooks Synchronous methods (`toHashSync`, `toNumberSync`) support hooks, but they fire asynchronously in the background to maintain the synchronous nature of the methods. ```typescript const hashery = new Hashery(); // Listen to synchronous hash hooks hashery.onHook('before:toHashSync', async (context) => { console.log('About to hash synchronously:', context.data); console.log('Using algorithm:', context.algorithm); }); hashery.onHook('after:toHashSync', async (result) => { console.log('Sync hash generated:', result.hash); }); // Call synchronous method - hooks fire in background const hash = hashery.toHashSync({ name: 'John', age: 30 }); console.log('Method returned:', hash); // Method returns immediately, hooks execute asynchronously ``` **Important Notes:** - Synchronous method hooks fire asynchronously (non-blocking) - The method returns immediately without waiting for hooks to complete - Hook modifications to context/result may not affect the returned value - For guaranteed hook execution, use async methods (`toHash`, `toNumber`) ### Why Are Sync Hooks Asynchronous? Synchronous methods are designed for maximum performance. Making hooks blocking would defeat this purpose. The async hook execution allows: - Logging and monitoring without performance impact - Side effects (like caching) without blocking - Maintaining the synchronous contract of the method If you need hooks that modify data or behavior, use the async methods (`toHash`, `toNumber`). ## Removing Hooks You can remove hooks when they're no longer needed: ```typescript const hashery = new Hashery(); const myHook = async (context: any) => { console.log('Hook called'); }; // Add the hook hashery.onHook('before:toHash', myHook); // Remove the hook hashery.offHook('before:toHash', myHook); // Same works for sync hooks hashery.onHook('before:toHashSync', myHook); hashery.offHook('before:toHashSync', myHook); ``` ## Error Handling in Hooks Control how errors in hooks are handled using the `throwOnEmitError` option: ```typescript // Throw errors that occur in hooks const hashery1 = new Hashery({ throwOnEmitError: true }); hashery1.onHook('before:toHash', async (context) => { throw new Error('Hook error'); }); // This will throw the error await hashery1.toHash({ data: 'example' }); // Throws Error: Hook error // Silently handle errors in hooks const hashery2 = new Hashery({ throwOnEmitError: false }); hashery2.onHook('before:toHash', async (context) => { throw new Error('Hook error'); }); // This will not throw, hashing continues const hash = await hashery2.toHash({ data: 'example' }); // Returns hash successfully ``` # Caching Hashery includes a built-in FIFO (First In, First Out) cache that stores computed hash values. When the same data is hashed with the same algorithm, the cached result is returned instead of recomputing. - **Enabled by Default** - Caching works out of the box - **FIFO Eviction** - Oldest entries removed when `maxSize` is reached (default: 4000) - **Shared Cache** - Both sync and async methods share the same cache ## Configuration ```typescript import { Hashery } from 'hashery'; // Default: cache enabled with maxSize of 4000 const hashery = new Hashery(); // Custom configuration const hashery2 = new Hashery({ cache: { enabled: true, // default: true maxSize: 10000 // default: 4000 } }); // Disable cache const hashery3 = new Hashery({ cache: { enabled: false } }); // Toggle at runtime hashery.cache.enabled = false; hashery.cache.enabled = true; ``` ## Cache Properties and Methods ```typescript const hashery = new Hashery(); hashery.cache.enabled; // boolean - is caching enabled hashery.cache.maxSize; // number - maximum entries (default: 4000) hashery.cache.size; // number - current cached entries hashery.cache.store; // Map<string, string> - underlying store hashery.cache.clear(); // Clear all cached entries ``` ## Memory Considerations The default `maxSize` of 4000 provides a good balance (~1-2 MB memory). JavaScript Maps can hold up to 2^24 (~16.7 million) entries, but practical limits depend on available memory. For most use cases, 4000-10000 entries is sufficient. # Web Crypto Hashery is built on top of the Web Crypto API, which provides cryptographic operations in both browser and Node.js environments. This ensures consistent, secure hashing across all platforms. ## Browser Support The Web Crypto API is supported in all modern browsers: - Chrome 37+ - Firefox 34+ - Safari 11+ - Edge 12+ ## Node.js Support Node.js 15+ includes the Web Crypto API via the `crypto.webcrypto` global. Hashery automatically detects and uses the appropriate crypto implementation for your environment. ## Available Algorithms ### Web Crypto Algorithms (Async Only) These algorithms use the Web Crypto API and are only available asynchronously: - **SHA-256** - Secure Hash Algorithm 256-bit (default for async methods) - **SHA-384** - Secure Hash Algorithm 384-bit - **SHA-512** - Secure Hash Algorithm 512-bit These are cryptographically secure and suitable for security-sensitive applications. ### Non-Crypto Algorithms (Async & Sync) These algorithms support both synchronous and asynchronous operation: - **djb2** - Fast hash function by Daniel J. Bernstein (default for sync methods) - **fnv1** - Fowler-Noll-Vo hash function - **murmer** - MurmurHash algorithm - **crc32** - Cyclic Redundancy Check 32-bit **Async methods** (`toHash`, `toNumber`): - Default to `SHA-256` - Can use any algorithm (WebCrypto or non-crypto) - Return Promises **Sync methods** (`toHashSync`, `toNumberSync`): - Default to `djb2` - Only work with non-crypto algorithms (djb2, fnv1, murmer, crc32) - Return values immediately - Throw an error if you try to use WebCrypto algorithms ## Example: Using Web Crypto ```typescript import { Hashery } from 'hashery'; const hashery = new Hashery(); // Web Crypto algorithms const sha256 = await hashery.toHash({ data: 'example' }); // Default SHA-256 const sha384 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-384' }); const sha512 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-512' }); // Non-crypto algorithms (faster, but not cryptographically secure) const djb2Hash = await hashery.toHash({ data: 'example' }, { algorithm: 'djb2' }); const fnv1Hash = await hashery.toHash({ data: 'example' }, { algorithm: 'fnv1' }); ``` # DJB2 Hashing DJB2 is a non-cryptographic hash function created by Daniel J. Bernstein. It's known for its simplicity and speed, making it ideal for hash tables, checksums, and other non-security applications. ## Why Use DJB2? - **Fast Performance** - Significantly faster than cryptographic hash functions - **Good Distribution** - Provides good hash distribution for most data - **Simple Algorithm** - Easy to understand and implement - **Low Collision Rate** - Works well for hash tables and data structures - **Deterministic** - Same input always produces the same output ## When to Use DJB2 **Good for:** - Hash tables and data structures - Non-security checksums - Fast data lookups - Cache keys - General-purpose hashing where security isn't a concern **Not suitable for:** - Password hashing - Cryptographic signatures - Security-sensitive applications - Data integrity verification where tampering is a concern ## DJB2 vs Cryptographic Hashes | Feature | DJB2 | SHA-256 | |---------|------|---------| | Speed | Very Fast | Slower | | Security | Not Secure | Cryptographically Secure | | Hash Length | 32-bit | 256-bit | | Collision Resistance | Good | Excellent | | Use Case | General Purpose | Security | ## Example: Using DJB2 ```typescript import { Hashery } from 'hashery'; const hashery = new Hashery(); // Hash with DJB2 (fast, non-cryptographic) const djb2Hash = await hashery.toHash({ userId: 123, action: 'login' }, { algorithm: 'djb2' }); // Use for cache keys const cacheKey = await hashery.toHash({ endpoint: '/api/users', params: { page: 1, limit: 10 } }, { algorithm: 'djb2' }); // Generate slot numbers with DJB2 const slot = await hashery.toNumber({ userId: 'user123' }, { min: 0, max: 99, algorithm: 'djb2' }); ``` ## Algorithm Details DJB2 uses a simple formula: ``` hash = 5381 for each character c: hash = ((hash << 5) + hash) + c ``` This translates to: `hash * 33 + c`, where 5381 is the magic initial value chosen by Daniel J. Bernstein for its distribution properties. # FNV1 Hashing FNV1 (Fowler-Noll-Vo) is a non-cryptographic hash function designed for fast hash table and checksum use. Created by Glenn Fowler, Landon Curt Noll, and Kiem-Phong Vo, it's known for its excellent distribution properties and simplicity. ## Why Use FNV1? - **Excellent Distribution** - Superior hash distribution reduces collisions - **Fast Performance** - Very fast computation with minimal operations - **Simple Implementation** - Easy to understand and implement - **Public Domain** - No licensing restrictions - **Well-Tested** - Extensively used and tested in production systems - **Deterministic** - Same input always produces the same output ## When to Use FNV1 **Good for:** - Hash tables and associative arrays - Checksums and fingerprints - Data deduplication - Bloom filters - Fast lookups and indexing - Non-cryptographic applications **Not suitable for:** - Password hashing - Cryptographic signatures - Security-critical applications - Digital signatures - Data integrity in adversarial environments ## FNV1 vs Other Hash Functions | Feature | FNV1 | DJB2 | SHA-256 | |---------|------|------|---------| | Speed | Very Fast | Very Fast | Slower | | Distribution | Excellent | Good | Excellent | | Security | Not Secure | Not Secure | Cryptographically Secure | | Collision Resistance | Good | Good | Excellent | | Use Case | Hash Tables | General Purpose | Security | ## Example: Using FNV1 ```typescript import { Hashery } from 'hashery'; const hashery = new Hashery(); // Hash with FNV1 (fast, excellent distribution) const fnv1Hash = await hashery.toHash({ productId: 'ABC123', variant: 'red' }, { algorithm: 'fnv1' }); // Use for hash table keys const tableKey = await hashery.toHash({ userId: 'user@example.com', resource: 'profile' }, { algorithm: 'fnv1' }); // Generate distributed slot numbers with FNV1 const slot = await hashery.toNumber({ sessionId: 'sess_xyz789' }, { min: 0, max: 999, algorithm: 'fnv1' }); // Use for data deduplication const fingerprint = await hashery.toHash({ content: 'document content here', metadata: { author: 'John', date: '2024-01-01' } }, { algorithm: 'fnv1' }); ``` ## Algorithm Details FNV1 uses the following formula: ``` hash = FNV_offset_basis for each byte b: hash = hash * FNV_prime hash = hash XOR b ``` Where: - **FNV_offset_basis**: Initial hash value (different for 32-bit, 64-bit, etc.) - **FNV_prime**: A carefully chosen prime number for good distribution - **XOR**: Bitwise exclusive OR operation The algorithm multiplies by a prime and XORs with each input byte, creating excellent avalanche properties where small input changes result in very different hash values. # CRC Hashing CRC (Cyclic Redundancy Check) is a non-cryptographic hash function designed primarily for detecting accidental changes to data. CRC32 is a 32-bit variant widely used in network protocols, file formats, and data integrity verification. ## Why Use CRC? - **Error Detection** - Excellent at detecting accidental data corruption - **Industry Standard** - Widely used in ZIP, PNG, Ethernet, and many other standards - **Fast Performance** - Very efficient computation using lookup tables - **Hardware Support** - Often implemented in hardware for maximum speed - **Well-Understood** - Decades of use and mathematical analysis - **Deterministic** - Same input always produces the same output ## When to Use CRC **Good for:** - Data integrity verification - Error detection in network protocols - File format checksums (ZIP, PNG, etc.) - Storage integrity checks - Detecting accidental corruption - Quick data validation **Not suitable for:** - Cryptographic applications - Password hashing - Digital signatures - Security-sensitive checksums - Protection against intentional tampering - Hash tables (not designed for this use case) ## CRC vs Other Hash Functions | Feature | CRC32 | DJB2 | FNV1 | SHA-256 | |---------|-------|------|------|---------| | Primary Use | Error Detection | Hash Tables | Hash Tables | Security | | Speed | Very Fast | Very Fast | Very Fast | Slower | | Security | Not Secure | Not Secure | Not Secure | Cryptographically Secure | | Hash Length | 32-bit | 32-bit | 32-bit/64-bit | 256-bit | | Error Detection | Excellent | Poor | Poor | Excellent | | Use Case | Data Integrity | General Purpose | Hash Tables | Security | ## Example: Using CRC ```typescript import { Hashery } from 'hashery'; const hashery = new Hashery(); // Hash with CRC32 for data integrity const crcHash = await hashery.toHash({ fileData: 'content here' }, { algorithm: 'crc32' }); // Verify file integrity const fileChecksum = await hashery.toHash({ filename: 'document.pdf', size: 1024000, modified: '2024-01-01' }, { algorithm: 'crc32' }); // Network packet validation const packetChecksum = await hashery.toHash({ header: { type: 'data', seq: 123 }, payload: 'packet payload data' }, { algorithm: 'crc32' }); // Quick data validation const dataIntegrity = await hashery.toHash({ recordId: 'rec_123', data: { field1: 'value1', field2: 'value2' } }, { algorithm: 'crc32' }); ``` ## Algorithm Details CRC32 uses polynomial division in a finite field (GF(2)): ``` CRC32 polynomial: 0x04C11DB7 (IEEE 802.3 standard) for each byte b: crc = (crc >> 8) XOR table[(crc XOR b) & 0xFF] ``` Key characteristics: - **Polynomial**: Uses a standardized polynomial for consistent results - **Lookup Table**: Pre-computed table for fast calculation - **Bit Shifting**: Efficient XOR and shift operations - **Finite Field**: Mathematical properties ensure good error detection ## Important Notes ⚠️ **Security Warning**: CRC is NOT cryptographically secure. It's designed to detect accidental errors, not intentional tampering. For security applications, use SHA-256 or other cryptographic hash functions. ✅ **Best Practice**: Use CRC32 for checksums and error detection in non-adversarial environments. Use cryptographic hashes (SHA-256, SHA-512) when security matters. # API - Properties ## `parse` Gets or sets the parse function used to deserialize stored values. **Type:** `ParseFn` **Default:** `JSON.parse` ```typescript const hashery = new Hashery(); hashery.parse = customParseFunction; ``` ## `stringify` Gets or sets the stringify function used to serialize values for storage. **Type:** `StringifyFn` **Default:** `JSON.stringify` ```typescript const hashery = new Hashery(); hashery.stringify = customStringifyFunction; ``` ## `providers` Gets or sets the HashProviders instance used to manage hash providers. **Type:** `HashProviders` ```typescript const hashery = new Hashery(); console.log(hashery.providers); ``` ## `names` Gets the names of all registered hash algorithm providers. **Type:** `Array<string>` **Returns:** An array of provider names (e.g., ['SHA-256', 'SHA-384', 'SHA-512', 'djb2', 'fnv1', 'murmer', 'crc32']) ```typescript const hashery = new Hashery(); console.log(hashery.names); // ['SHA-256', 'SHA-384', 'SHA-512', 'djb2', 'fnv1', 'murmer', 'crc32'] ``` ## `defaultAlgorithm` Gets or sets the default hash algorithm to use when none is specified for async methods. **Type:** `string` **Default:** `'SHA-256'` ```typescript const hashery = new Hashery(); // Get default algorithm console.log(hashery.defaultAlgorithm); // 'SHA-256' // Set default algorithm hashery.defaultAlgorithm = 'SHA-512'; // Now all async hashes use SHA-512 by default const hash = await hashery.toHash({ data: 'example' }); console.log(hash.length); // 128 (SHA-512 produces 128 hex characters) ``` ## `defaultAlgorithmSync` Gets or sets the default hash algorithm to use when none is specified for synchronous methods. **Type:** `string` **Default:** `'djb2'` ```typescript const hashery = new Hashery(); // Get default sync algorithm console.log(hashery.defaultAlgorithmSync); // 'djb2' // Set default sync algorithm hashery.defaultAlgorithmSync = 'fnv1'; // Now all sync hashes use fnv1 by default const hash = hashery.toHashSync({ data: 'example' }); // You can also set it in the constructor const hashery2 = new Hashery({ defaultAlgorithmSync: 'murmer' }); const hash2 = hashery2.toHashSync({ data: 'test' }); // Uses murmer ``` # API - Functions ## `toHash(data, options?)` Generates a cryptographic hash of the provided data using the specified algorithm (async). The data is first stringified using the configured stringify function, then hashed. **Parameters:** - `data` (unknown) - The data to hash (will be stringified before hashing) - `options` (object, optional) - Configuration options - `algorithm` (string, optional) - The hash algorithm to use (defaults to 'SHA-256') - `maxLength` (number, optional) - Maximum length for the hash output (truncates from the start) **Returns:** `Promise<string>` - A Promise that resolves to the hexadecimal string representation of the hash **Example:** ```typescript const hashery = new Hashery(); // Using default SHA-256 const hash = await hashery.toHash({ name: 'John', age: 30 }); // Using a different algorithm const hash512 = await hashery.toHash({ name: 'John' }, { algorithm: 'SHA-512' }); const fastHash = await hashery.toHash({ name: 'John' }, { algorithm: 'djb2' }); // Truncating hash output const shortHash = await hashery.toHash( { name: 'John' }, { algorithm: 'SHA-256', maxLength: 16 } ); ``` ## `toHashSync(data, options?)` Generates a hash of the provided data synchronously using a non-cryptographic hash algorithm. The data is first stringified using the configured stringify function, then hashed. **Important:** This method only works with synchronous hash providers (djb2, fnv1, murmer, crc32). WebCrypto algorithms (SHA-256, SHA-384, SHA-512) are not supported and will throw an error. **Parameters:** - `data` (unknown) - The data to hash (will be stringified before hashing) - `options` (object, optional) - Configuration options - `algorithm` (string, optional) - The hash algorithm to use (defaults to 'djb2') - `maxLength` (number, optional) - Maximum length for the hash output (truncates from the start) **Returns:** `string` - The hexadecimal string representation of the hash **Throws:** `Error` if the specified algorithm does not support synchronous hashing **Example:** ```typescript const hashery = new Hashery(); // Using default djb2 const hash = hashery.toHashSync({ name: 'John', age: 30 }); // Using a different algorithm const hashFnv1 = hashery.toHashSync({ name: 'John' }, { algorithm: 'fnv1' }); const hashMurmer = hashery.toHashSync({ name: 'John' }, { algorithm: 'murmer' }); const hashCrc = hashery.toHashSync({ name: 'John' }, { algorithm: 'crc32' }); // Truncating hash output const shortHash = hashery.toHashSync( { name: 'John' }, { algorithm: 'djb2', maxLength: 4 } ); // This will throw an error (WebCrypto not supported in sync mode) // const invalid = hashery.toHashSync({ name: 'John' }, { algorithm: 'SHA-256' }); // ❌ ``` ## `toNumber(data, options?)` Generates a deterministic number within a specified range based on the hash of the provided data (async). This method uses the toHash function to create a consistent hash, then maps it to a number between min and max (inclusive). **Parameters:** - `data` (unknown) - The data to hash (will be stringified before hashing) - `options` (object, optional) - Configuration options - `min` (number, optional) - The minimum value of the range (inclusive, defaults to 0) - `max` (number, optional) - The maximum value of the range (inclusive, defaults to 100) - `algorithm` (string, optional) - The hash algorithm to use (defaults to 'SHA-256') - `hashLength` (number, optional) - Number of characters from hash to use for conversion (defaults to 16) **Returns:** `Promise<number>` - A Promise that resolves to a number between min and max (inclusive) **Throws:** Error if min is greater than max **Example:** ```typescript const hashery = new Hashery(); // Generate a number between 0 and 100 (default range) const num = await hashery.toNumber({ user: 'john' }); // Generate a number with custom range const num2 = await hashery.toNumber({ user: 'john' }, { min: 0, max: 100 }); // Using a different algorithm const num512 = await hashery.toNumber({ user: 'john' }, { min: 0, max: 255, algorithm: 'SHA-512' }); ``` ## `toNumberSync(data, options?)` Generates a deterministic number within a specified range based on the hash of the provided data synchronously. This method uses the toHashSync function to create a consistent hash, then maps it to a number between min and max (inclusive). **Important:** This method only works with synchronous hash providers (djb2, fnv1, murmer, crc32). **Parameters:** - `data` (unknown) - The data to hash (will be stringified before hashing) - `options` (object, optional) - Configuration options - `min` (number, optional) - The minimum value of the range (inclusive, defaults to 0) - `max` (number, optional) - The maximum value of the range (inclusive, defaults to 100) - `algorithm` (string, optional) - The hash algorithm to use (defaults to 'djb2') - `hashLength` (number, optional) - Number of characters from hash to use for conversion (defaults to 16) **Returns:** `number` - A number between min and max (inclusive) **Throws:** - Error if min is greater than max - Error if the specified algorithm does not support synchronous hashing **Example:** ```typescript const hashery = new Hashery(); // Generate a number between 0 and 100 (default range) const num = hashery.toNumberSync({ user: 'john' }); // Generate a number with custom range const slot = hashery.toNumberSync({ user: 'john' }, { min: 0, max: 9 }); // Using a different algorithm const numFnv1 = hashery.toNumberSync({ user: 'john' }, { min: 0, max: 255, algorithm: 'fnv1' }); // A/B testing const variant = hashery.toNumberSync({ userId: 'user123' }, { min: 0, max: 1 }); console.log(variant === 0 ? 'Group A' : 'Group B'); // Load balancing const serverId = hashery.toNumberSync( { requestId: 'req_abc' }, { min: 0, max: 9, algorithm: 'murmer' } // 10 servers ); // This will throw an error (WebCrypto not supported in sync mode) // const invalid = hashery.toNumberSync({ user: 'john' }, { algorithm: 'SHA-256' }); // ❌ ``` ## `loadProviders(providers?, options?)` Loads hash providers into the Hashery instance. This allows you to add custom hash providers or replace the default ones. **Parameters:** - `providers` (Array<HashProvider>, optional) - Array of hash providers to add - `options` (HasheryLoadProviderOptions, optional) - Options object - `includeBase` (boolean) - Whether to include base providers (default: true) **Returns:** `void` **Example:** ```typescript const hashery = new Hashery(); // Add a custom provider const customProvider = { name: 'custom', toHash: async (data: BufferSource) => 'custom-hash' }; hashery.loadProviders([customProvider]); // Load without base providers hashery.loadProviders([customProvider], { includeBase: false }); ``` # Benchmarks Overall view of the current algorithm's and their performance using simple hashing with random data. `Sync` is when we use `toHashSync` and `Async` is the `toHash` function which requires `await`. **NOTE: Many of these are not secure and should be used only for object hashing. Read about each one in the documentation and pick what works best for your use case.** ## Hashing | name | summary | ops/sec | time/op | margin | samples | |-----------------|:---------:|----------:|----------:|:--------:|----------:| | DJB2 Sync | 🥇 | 708K | 1µs | ±1.54% | 669K | | FNV1 Sync | -0.21% | 707K | 2µs | ±1.30% | 665K | | MURMER Sync | -1.2% | 700K | 2µs | ±0.60% | 653K | | CRC32 Sync | -1.2% | 700K | 2µs | ±1.55% | 652K | | CRC32 Async | -11% | 630K | 2µs | ±0.50% | 598K | | DJB2 Async | -11% | 629K | 2µs | ±0.55% | 597K | | FNV1 Async | -12% | 623K | 2µs | ±0.82% | 581K | | SHA-512 Async | -14% | 612K | 2µs | ±0.47% | 548K | | SHA-384 Async | -14% | 611K | 2µs | ±0.51% | 549K | | MURMER Async | -14% | 608K | 2µs | ±0.61% | 569K | | SHA-256 Async | -16% | 593K | 2µs | ±0.52% | 522K | ## Hashing without Caching | name | summary | ops/sec | time/op | margin | samples | |-----------------|:---------:|----------:|----------:|:--------:|----------:| | DJB2 Sync | 🥇 | 542K | 2µs | ±0.28% | 514K | | MURMER Sync | -1.5% | 533K | 2µs | ±0.71% | 499K | | FNV1 Sync | -12% | 478K | 2µs | ±0.33% | 456K | | DJB2 Async | -12% | 477K | 2µs | ±0.38% | 451K | | MURMER Async | -13% | 471K | 2µs | ±0.40% | 447K | | FNV1 Async | -22% | 425K | 2µs | ±1.32% | 403K | | CRC32 Sync | -37% | 342K | 3µs | ±0.84% | 329K | | CRC32 Async | -38% | 338K | 3µs | ±0.34% | 326K | | SHA-256 Async | -88% | 67K | 16µs | ±0.69% | 63K | | SHA-384 Async | -89% | 62K | 17µs | ±0.45% | 59K | | SHA-512 Async | -89% | 59K | 18µs | ±0.63% | 55K | Caching is enabled by default and is a simple FIFO with default max keys at `4000`. The performance gain is greater than 20%+ on average. Some of the biggest gains are on `SHA` hashing which 10x when caching is enabled. # Code of Conduct and Contributing Please use our [Code of Conduct](CODE_OF_CONDUCT.md) and [Contributing](CONTRIBUTING.md) guidelines for development and testing. We appreciate your contributions! # License and Copyright [MIT](LICENSE) & © [Jared Wray](https://jaredwray.com)