@alwatr/hash-string 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +37 -15
- package/dist/djb2-hash.d.ts +21 -0
- package/dist/djb2-hash.d.ts.map +1 -0
- package/dist/main.cjs +23 -7
- package/dist/main.cjs.map +3 -3
- package/dist/main.d.ts +2 -11
- package/dist/main.d.ts.map +1 -1
- package/dist/main.mjs +21 -6
- package/dist/main.mjs.map +3 -3
- package/dist/nano-hash.d.ts +12 -0
- package/dist/nano-hash.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/djb2-hash.test.js +72 -0
- package/src/{main.test.js → nano-hash.test.js} +26 -18
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [5.2.0](https://github.com/Alwatr/nanolib/compare/@alwatr/hash-string@5.1.0...@alwatr/hash-string@5.2.0) (2025-04-01)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* implement DJB2 hash algorithm for efficient string hashing ([d505181](https://github.com/Alwatr/nanolib/commit/d505181fed1f85cf067ea5499b3de692b84385b0)) by @alimd
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* change default repeat value in hashString function from 3 to 1 ([e5c373f](https://github.com/Alwatr/nanolib/commit/e5c373f2a18ce1933f234a6f8dafeda181df1f14)) by @alimd
|
|
15
|
+
|
|
16
|
+
### Code Refactoring
|
|
17
|
+
|
|
18
|
+
* replace hashString function with nanoHash and export new hashing methods ([3651b0c](https://github.com/Alwatr/nanolib/commit/3651b0cf254a044f7eb4cfc0a52a9b645e1c26d3)) by @alimd
|
|
19
|
+
|
|
6
20
|
## 5.1.0 (2025-03-18)
|
|
7
21
|
|
|
8
22
|
### Features
|
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# hash-string
|
|
2
2
|
|
|
3
|
-
A lightweight, high-performance utility for generating simple
|
|
3
|
+
A lightweight, high-performance utility for generating simple hash strings from input values.
|
|
4
|
+
It is **non-cryptographic** but very fast and efficient. While it cannot be reversed easily and brute force attacks can take up to years for fast computers.
|
|
4
5
|
|
|
5
6
|
## Installation
|
|
6
7
|
|
|
@@ -13,22 +14,22 @@ npm install @alwatr/hash-string
|
|
|
13
14
|
## Usage
|
|
14
15
|
|
|
15
16
|
```typescript
|
|
16
|
-
import {
|
|
17
|
+
import {nanoHash} from '@alwatr/hash-string';
|
|
17
18
|
|
|
18
19
|
// Hash a string with a prefix
|
|
19
|
-
|
|
20
|
+
nanoHash('test', 'prefix-'); // => 'prefix-j26j3d4'
|
|
20
21
|
|
|
21
22
|
// Hash a number
|
|
22
|
-
|
|
23
|
+
nanoHash(12345, 'num-'); // => 'num-8hu3f2l'
|
|
23
24
|
|
|
24
25
|
// Adjust complexity with repeat parameter
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
nanoHash('test', 'p-', 1); // => 'p-7ba2n3y' (faster, less complex)
|
|
27
|
+
nanoHash('test', 'p-', 5); // => 'p-3f72h9b' (slower, more complex)
|
|
27
28
|
```
|
|
28
29
|
|
|
29
30
|
## API
|
|
30
31
|
|
|
31
|
-
###
|
|
32
|
+
### nanoHash(str: string | number, prefix: string, repeat = 3): string
|
|
32
33
|
|
|
33
34
|
Generates a simple hash from the input string or number.
|
|
34
35
|
|
|
@@ -49,7 +50,9 @@ Returns a hashed string with the specified prefix.
|
|
|
49
50
|
|
|
50
51
|
## Security Note
|
|
51
52
|
|
|
52
|
-
This function is designed for simple hashing needs like generating IDs or checksums. It is **not suitable** for
|
|
53
|
+
This function is designed for simple hashing needs like generating IDs or checksums. It is **non-cryptographic** but very fast and efficient. While it cannot be reversed easily and brute force attacks can take up to years for fast computers, it is still **not suitable** for security-sensitive applications or storing sensitive data. The algorithm prioritizes speed and simplicity over cryptographic strength, making it ideal for general-purpose hashing where security is not a primary concern.
|
|
54
|
+
|
|
55
|
+
For security-critical applications, use established cryptographic hash functions (like SHA-256 or Argon2) instead.
|
|
53
56
|
|
|
54
57
|
## Examples
|
|
55
58
|
|
|
@@ -57,26 +60,26 @@ This function is designed for simple hashing needs like generating IDs or checks
|
|
|
57
60
|
|
|
58
61
|
```typescript
|
|
59
62
|
// Generate a hash for a string
|
|
60
|
-
|
|
63
|
+
nanoHash('hello world', 'msg-'); // => 'msg-k7f2h9d'
|
|
61
64
|
|
|
62
65
|
// Generate a hash for a number
|
|
63
|
-
|
|
66
|
+
nanoHash(42, 'id-'); // => 'id-p83b2e4'
|
|
64
67
|
|
|
65
68
|
// Same input produces the same output
|
|
66
|
-
|
|
69
|
+
nanoHash('test', 'x-') === nanoHash('test', 'x-'); // => true
|
|
67
70
|
|
|
68
71
|
// Different inputs produce different outputs
|
|
69
|
-
|
|
72
|
+
nanoHash('test1', 'x-') !== nanoHash('test2', 'x-'); // => true
|
|
70
73
|
```
|
|
71
74
|
|
|
72
75
|
### Controlling Complexity
|
|
73
76
|
|
|
74
77
|
```typescript
|
|
75
78
|
// Less complex (faster)
|
|
76
|
-
|
|
79
|
+
nanoHash('password', 'user-', 1);
|
|
77
80
|
|
|
78
81
|
// More complex (slightly slower)
|
|
79
|
-
|
|
82
|
+
nanoHash('password', 'user-', 5);
|
|
80
83
|
```
|
|
81
84
|
|
|
82
85
|
### Use Cases
|
|
@@ -88,7 +91,7 @@ hashString('password', 'user-', 5);
|
|
|
88
91
|
|
|
89
92
|
## Implementation Details
|
|
90
93
|
|
|
91
|
-
The hashing algorithm combines two 32-bit hash functions with prime number multipliers to create a distribution with good avalanche properties. This means small changes in the input produce significantly different outputs, reducing collision probability.
|
|
94
|
+
The hashing algorithm combines two 32-bit hash functions with prime number multipliers to create a distribution with good avalanche properties. This means small changes in the input produce significantly different outputs, reducing collision probability. Though non-cryptographic, the algorithm's computational complexity makes it resistant to casual reversal attempts.
|
|
92
95
|
|
|
93
96
|
The implementation:
|
|
94
97
|
|
|
@@ -98,6 +101,25 @@ The implementation:
|
|
|
98
101
|
4. Optionally repeats the process for increased complexity
|
|
99
102
|
5. Converts the result to base-36 for compact representation
|
|
100
103
|
|
|
104
|
+
### DJB2 Hash Algorithm
|
|
105
|
+
|
|
106
|
+
The package includes the DJB2 hash algorithm, a fast and efficient string hashing function created by Daniel J. Bernstein:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// Generate a numeric hash value
|
|
110
|
+
import {djb2Hash} from '@alwatr/hash-string';
|
|
111
|
+
|
|
112
|
+
const hashValue = djb2Hash("hello world"); // Returns a 32-bit unsigned integer
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Key features of djb2Hash:
|
|
116
|
+
|
|
117
|
+
- Fast computation with minimal operations
|
|
118
|
+
- Produces consistent 32-bit unsigned integer values
|
|
119
|
+
- Good distribution for short to medium-length strings
|
|
120
|
+
- Simple implementation with right-to-left iteration for performance
|
|
121
|
+
- Uses prime number (5381) as the initial seed
|
|
122
|
+
|
|
101
123
|
## Sponsors
|
|
102
124
|
|
|
103
125
|
The following companies, organizations, and individuals support Nanolib ongoing maintenance and development. Become a Sponsor to get your logo on our README and website.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DJB2 Hash Algorithm - A fast string hashing function.
|
|
3
|
+
*
|
|
4
|
+
* This implementation is based on Daniel J. Bernstein's popular 'times 33' hash algorithm,
|
|
5
|
+
* commonly known as DJB2. It's known for its simplicity, speed, and good distribution properties
|
|
6
|
+
* for short strings, making it suitable for general purpose hashing needs.
|
|
7
|
+
*
|
|
8
|
+
* Performance notes:
|
|
9
|
+
* - Uses right-to-left iteration to avoid repeated length lookups
|
|
10
|
+
* - Employs bit shifting operations for faster computation
|
|
11
|
+
* - Final right shift ensures unsigned 32-bit integer output
|
|
12
|
+
*
|
|
13
|
+
* @param {string} str - The input string to be hashed
|
|
14
|
+
* @returns {number} A 32-bit unsigned integer hash value
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Returns a numeric hash value
|
|
18
|
+
* const hashValue = djb2Hash("hello world");
|
|
19
|
+
*/
|
|
20
|
+
export declare function djb2Hash(str: string): number;
|
|
21
|
+
//# sourceMappingURL=djb2-hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"djb2-hash.d.ts","sourceRoot":"","sources":["../src/djb2-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAY5C"}
|
package/dist/main.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* @alwatr/hash-string v5.
|
|
1
|
+
/* @alwatr/hash-string v5.2.0 */
|
|
2
2
|
"use strict";
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -21,12 +21,23 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
// src/main.ts
|
|
22
22
|
var main_exports = {};
|
|
23
23
|
__export(main_exports, {
|
|
24
|
-
|
|
24
|
+
djb2Hash: () => djb2Hash,
|
|
25
|
+
nanoHash: () => nanoHash
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(main_exports);
|
|
27
28
|
var import_package_tracer = require("@alwatr/package-tracer");
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
|
|
30
|
+
// src/djb2-hash.ts
|
|
31
|
+
function djb2Hash(str) {
|
|
32
|
+
let hashValue = 5381;
|
|
33
|
+
for (let i = str.length - 1; i >= 0; i--) {
|
|
34
|
+
hashValue = (hashValue << 5) + hashValue ^ str.charCodeAt(i);
|
|
35
|
+
}
|
|
36
|
+
return hashValue >>> 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/nano-hash.ts
|
|
40
|
+
function nanoHash(str, prefix, repeat = 1) {
|
|
30
41
|
if (repeat < 1) {
|
|
31
42
|
throw new Error("The repeat parameter must be greater than or equal to 1");
|
|
32
43
|
}
|
|
@@ -35,7 +46,8 @@ function hashString(str, prefix, repeat = 3) {
|
|
|
35
46
|
if (typeof str === "number") {
|
|
36
47
|
str = str.toString();
|
|
37
48
|
}
|
|
38
|
-
|
|
49
|
+
const len = str.length;
|
|
50
|
+
for (let i = 0; i < len; i++) {
|
|
39
51
|
const char = str.charCodeAt(i);
|
|
40
52
|
hash1 = Math.imul(hash1 ^ char, 2654435761);
|
|
41
53
|
hash2 = Math.imul(hash2 ^ char, 1597334677);
|
|
@@ -46,11 +58,15 @@ function hashString(str, prefix, repeat = 3) {
|
|
|
46
58
|
if (repeat === 1) {
|
|
47
59
|
return result;
|
|
48
60
|
} else {
|
|
49
|
-
return
|
|
61
|
+
return nanoHash(result, prefix, repeat - 1);
|
|
50
62
|
}
|
|
51
63
|
}
|
|
64
|
+
|
|
65
|
+
// src/main.ts
|
|
66
|
+
__dev_mode__: import_package_tracer.packageTracer.add("@alwatr/hash-string", "5.2.0");
|
|
52
67
|
// Annotate the CommonJS export names for ESM import in node:
|
|
53
68
|
0 && (module.exports = {
|
|
54
|
-
|
|
69
|
+
djb2Hash,
|
|
70
|
+
nanoHash
|
|
55
71
|
});
|
|
56
72
|
//# sourceMappingURL=main.cjs.map
|
package/dist/main.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/main.ts"],
|
|
4
|
-
"sourcesContent": ["import {packageTracer} from '@alwatr/package-tracer';\n\n__dev_mode__: packageTracer.add(__package_name__, __package_version__);\n\n/**\n * Simple hash string for fast hashing (like md5).\n * This function is not very secure and should not be used for security purposes.\n * But it cannot be reversed easily and brute force can take up to years for
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAA4B;
|
|
3
|
+
"sources": ["../src/main.ts", "../src/djb2-hash.ts", "../src/nano-hash.ts"],
|
|
4
|
+
"sourcesContent": ["import {packageTracer} from '@alwatr/package-tracer';\n\n__dev_mode__: packageTracer.add(__package_name__, __package_version__);\n\nexport * from './djb2-hash.js';\nexport * from './nano-hash.js';\n", "/**\n * DJB2 Hash Algorithm - A fast string hashing function.\n *\n * This implementation is based on Daniel J. Bernstein's popular 'times 33' hash algorithm,\n * commonly known as DJB2. It's known for its simplicity, speed, and good distribution properties\n * for short strings, making it suitable for general purpose hashing needs.\n *\n * Performance notes:\n * - Uses right-to-left iteration to avoid repeated length lookups\n * - Employs bit shifting operations for faster computation\n * - Final right shift ensures unsigned 32-bit integer output\n *\n * @param {string} str - The input string to be hashed\n * @returns {number} A 32-bit unsigned integer hash value\n *\n * @example\n * // Returns a numeric hash value\n * const hashValue = djb2Hash(\"hello world\");\n */\nexport function djb2Hash(str: string): number {\n // 5381 is a prime number used as initial value in the DJB2 algorithm\n let hashValue = 5381;\n\n // Reverse loop for better performance - avoids repeated length property lookup\n for (let i = str.length - 1; i >= 0; i--) {\n // Using left shift (*2) and addition instead of multiplication by 33\n // (hash * 33) is equivalent to ((hash << 5) + hash)\n hashValue = ((hashValue << 5) + hashValue) ^ str.charCodeAt(i);\n }\n\n return hashValue >>> 0;\n}\n", "/**\n * Simple hash string for fast hashing (like md5).\n * This function is not very secure and should not be used for security purposes.\n * But it cannot be reversed easily and brute force can take up to years for fast computers.\n *\n * @param str - The string or number to hash\n * @param prefix - A prefix to add to the beginning of the hash result\n * @param repeat - Number of times to repeat the hashing process for increased complexity (default: 3)\n * @returns A hashed string with the specified prefix\n */\nexport function nanoHash(str: string | number, prefix: string, repeat = 1): string {\n if (repeat < 1) {\n throw new Error('The repeat parameter must be greater than or equal to 1');\n }\n\n let hash1 = 0xdeadbeef;\n let hash2 = 0x41c6ce57;\n\n if (typeof str === 'number') {\n str = str.toString();\n }\n\n const len = str.length;\n for (let i = 0; i < len; i++) {\n const char = str.charCodeAt(i);\n hash1 = Math.imul(hash1 ^ char, 2654435761);\n hash2 = Math.imul(hash2 ^ char, 1597334677);\n }\n\n hash1 = Math.imul(hash1 ^ (hash1 >>> 16), 2246822507) ^ Math.imul(hash2 ^ (hash2 >>> 13), 3266489909);\n hash2 = Math.imul(hash2 ^ (hash2 >>> 16), 2246822507) ^ Math.imul(hash1 ^ (hash1 >>> 13), 3266489909);\n\n const result = prefix + (hash1 >>> 0).toString(36) + (hash2 >>> 0).toString(36);\n if (repeat === 1) {\n return result;\n }\n else {\n return nanoHash(result, prefix, repeat - 1);\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAA4B;;;ACmBrB,SAAS,SAAS,KAAqB;AAE5C,MAAI,YAAY;AAGhB,WAAS,IAAI,IAAI,SAAS,GAAG,KAAK,GAAG,KAAK;AAGxC,iBAAc,aAAa,KAAK,YAAa,IAAI,WAAW,CAAC;AAAA,EAC/D;AAEA,SAAO,cAAc;AACvB;;;ACrBO,SAAS,SAAS,KAAsB,QAAgB,SAAS,GAAW;AACjF,MAAI,SAAS,GAAG;AACd,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AAEA,MAAI,QAAQ;AACZ,MAAI,QAAQ;AAEZ,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,SAAS;AAAA,EACrB;AAEA,QAAM,MAAM,IAAI;AAChB,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,UAAM,OAAO,IAAI,WAAW,CAAC;AAC7B,YAAQ,KAAK,KAAK,QAAQ,MAAM,UAAU;AAC1C,YAAQ,KAAK,KAAK,QAAQ,MAAM,UAAU;AAAA,EAC5C;AAEA,UAAQ,KAAK,KAAK,QAAS,UAAU,IAAK,UAAU,IAAI,KAAK,KAAK,QAAS,UAAU,IAAK,UAAU;AACpG,UAAQ,KAAK,KAAK,QAAS,UAAU,IAAK,UAAU,IAAI,KAAK,KAAK,QAAS,UAAU,IAAK,UAAU;AAEpG,QAAM,SAAS,UAAU,UAAU,GAAG,SAAS,EAAE,KAAK,UAAU,GAAG,SAAS,EAAE;AAC9E,MAAI,WAAW,GAAG;AAChB,WAAO;AAAA,EACT,OACK;AACH,WAAO,SAAS,QAAQ,QAAQ,SAAS,CAAC;AAAA,EAC5C;AACF;;;AFrCA,aAAc,qCAAc,IAAI,uBAAkB,OAAmB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/main.d.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
* This function is not very secure and should not be used for security purposes.
|
|
4
|
-
* But it cannot be reversed easily and brute force can take up to years for very fast computers.
|
|
5
|
-
*
|
|
6
|
-
* @param str - The string or number to hash
|
|
7
|
-
* @param prefix - A prefix to add to the beginning of the hash result
|
|
8
|
-
* @param repeat - Number of times to repeat the hashing process for increased complexity (default: 3)
|
|
9
|
-
* @returns A hashed string with the specified prefix
|
|
10
|
-
*/
|
|
11
|
-
export declare function hashString(str: string | number, prefix: string, repeat?: number): string;
|
|
1
|
+
export * from './djb2-hash.js';
|
|
2
|
+
export * from './nano-hash.js';
|
|
12
3
|
//# sourceMappingURL=main.d.ts.map
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAIA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
|
package/dist/main.mjs
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
/* @alwatr/hash-string v5.
|
|
1
|
+
/* @alwatr/hash-string v5.2.0 */
|
|
2
2
|
|
|
3
3
|
// src/main.ts
|
|
4
4
|
import { packageTracer } from "@alwatr/package-tracer";
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
|
|
6
|
+
// src/djb2-hash.ts
|
|
7
|
+
function djb2Hash(str) {
|
|
8
|
+
let hashValue = 5381;
|
|
9
|
+
for (let i = str.length - 1; i >= 0; i--) {
|
|
10
|
+
hashValue = (hashValue << 5) + hashValue ^ str.charCodeAt(i);
|
|
11
|
+
}
|
|
12
|
+
return hashValue >>> 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/nano-hash.ts
|
|
16
|
+
function nanoHash(str, prefix, repeat = 1) {
|
|
7
17
|
if (repeat < 1) {
|
|
8
18
|
throw new Error("The repeat parameter must be greater than or equal to 1");
|
|
9
19
|
}
|
|
@@ -12,7 +22,8 @@ function hashString(str, prefix, repeat = 3) {
|
|
|
12
22
|
if (typeof str === "number") {
|
|
13
23
|
str = str.toString();
|
|
14
24
|
}
|
|
15
|
-
|
|
25
|
+
const len = str.length;
|
|
26
|
+
for (let i = 0; i < len; i++) {
|
|
16
27
|
const char = str.charCodeAt(i);
|
|
17
28
|
hash1 = Math.imul(hash1 ^ char, 2654435761);
|
|
18
29
|
hash2 = Math.imul(hash2 ^ char, 1597334677);
|
|
@@ -23,10 +34,14 @@ function hashString(str, prefix, repeat = 3) {
|
|
|
23
34
|
if (repeat === 1) {
|
|
24
35
|
return result;
|
|
25
36
|
} else {
|
|
26
|
-
return
|
|
37
|
+
return nanoHash(result, prefix, repeat - 1);
|
|
27
38
|
}
|
|
28
39
|
}
|
|
40
|
+
|
|
41
|
+
// src/main.ts
|
|
42
|
+
__dev_mode__: packageTracer.add("@alwatr/hash-string", "5.2.0");
|
|
29
43
|
export {
|
|
30
|
-
|
|
44
|
+
djb2Hash,
|
|
45
|
+
nanoHash
|
|
31
46
|
};
|
|
32
47
|
//# sourceMappingURL=main.mjs.map
|
package/dist/main.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/main.ts"],
|
|
4
|
-
"sourcesContent": ["import {packageTracer} from '@alwatr/package-tracer';\n\n__dev_mode__: packageTracer.add(__package_name__, __package_version__);\n\n/**\n * Simple hash string for fast hashing (like md5).\n * This function is not very secure and should not be used for security purposes.\n * But it cannot be reversed easily and brute force can take up to years for
|
|
5
|
-
"mappings": ";;;AAAA,SAAQ,qBAAoB;
|
|
3
|
+
"sources": ["../src/main.ts", "../src/djb2-hash.ts", "../src/nano-hash.ts"],
|
|
4
|
+
"sourcesContent": ["import {packageTracer} from '@alwatr/package-tracer';\n\n__dev_mode__: packageTracer.add(__package_name__, __package_version__);\n\nexport * from './djb2-hash.js';\nexport * from './nano-hash.js';\n", "/**\n * DJB2 Hash Algorithm - A fast string hashing function.\n *\n * This implementation is based on Daniel J. Bernstein's popular 'times 33' hash algorithm,\n * commonly known as DJB2. It's known for its simplicity, speed, and good distribution properties\n * for short strings, making it suitable for general purpose hashing needs.\n *\n * Performance notes:\n * - Uses right-to-left iteration to avoid repeated length lookups\n * - Employs bit shifting operations for faster computation\n * - Final right shift ensures unsigned 32-bit integer output\n *\n * @param {string} str - The input string to be hashed\n * @returns {number} A 32-bit unsigned integer hash value\n *\n * @example\n * // Returns a numeric hash value\n * const hashValue = djb2Hash(\"hello world\");\n */\nexport function djb2Hash(str: string): number {\n // 5381 is a prime number used as initial value in the DJB2 algorithm\n let hashValue = 5381;\n\n // Reverse loop for better performance - avoids repeated length property lookup\n for (let i = str.length - 1; i >= 0; i--) {\n // Using left shift (*2) and addition instead of multiplication by 33\n // (hash * 33) is equivalent to ((hash << 5) + hash)\n hashValue = ((hashValue << 5) + hashValue) ^ str.charCodeAt(i);\n }\n\n return hashValue >>> 0;\n}\n", "/**\n * Simple hash string for fast hashing (like md5).\n * This function is not very secure and should not be used for security purposes.\n * But it cannot be reversed easily and brute force can take up to years for fast computers.\n *\n * @param str - The string or number to hash\n * @param prefix - A prefix to add to the beginning of the hash result\n * @param repeat - Number of times to repeat the hashing process for increased complexity (default: 3)\n * @returns A hashed string with the specified prefix\n */\nexport function nanoHash(str: string | number, prefix: string, repeat = 1): string {\n if (repeat < 1) {\n throw new Error('The repeat parameter must be greater than or equal to 1');\n }\n\n let hash1 = 0xdeadbeef;\n let hash2 = 0x41c6ce57;\n\n if (typeof str === 'number') {\n str = str.toString();\n }\n\n const len = str.length;\n for (let i = 0; i < len; i++) {\n const char = str.charCodeAt(i);\n hash1 = Math.imul(hash1 ^ char, 2654435761);\n hash2 = Math.imul(hash2 ^ char, 1597334677);\n }\n\n hash1 = Math.imul(hash1 ^ (hash1 >>> 16), 2246822507) ^ Math.imul(hash2 ^ (hash2 >>> 13), 3266489909);\n hash2 = Math.imul(hash2 ^ (hash2 >>> 16), 2246822507) ^ Math.imul(hash1 ^ (hash1 >>> 13), 3266489909);\n\n const result = prefix + (hash1 >>> 0).toString(36) + (hash2 >>> 0).toString(36);\n if (repeat === 1) {\n return result;\n }\n else {\n return nanoHash(result, prefix, repeat - 1);\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;AAAA,SAAQ,qBAAoB;;;ACmBrB,SAAS,SAAS,KAAqB;AAE5C,MAAI,YAAY;AAGhB,WAAS,IAAI,IAAI,SAAS,GAAG,KAAK,GAAG,KAAK;AAGxC,iBAAc,aAAa,KAAK,YAAa,IAAI,WAAW,CAAC;AAAA,EAC/D;AAEA,SAAO,cAAc;AACvB;;;ACrBO,SAAS,SAAS,KAAsB,QAAgB,SAAS,GAAW;AACjF,MAAI,SAAS,GAAG;AACd,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AAEA,MAAI,QAAQ;AACZ,MAAI,QAAQ;AAEZ,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,SAAS;AAAA,EACrB;AAEA,QAAM,MAAM,IAAI;AAChB,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,UAAM,OAAO,IAAI,WAAW,CAAC;AAC7B,YAAQ,KAAK,KAAK,QAAQ,MAAM,UAAU;AAC1C,YAAQ,KAAK,KAAK,QAAQ,MAAM,UAAU;AAAA,EAC5C;AAEA,UAAQ,KAAK,KAAK,QAAS,UAAU,IAAK,UAAU,IAAI,KAAK,KAAK,QAAS,UAAU,IAAK,UAAU;AACpG,UAAQ,KAAK,KAAK,QAAS,UAAU,IAAK,UAAU,IAAI,KAAK,KAAK,QAAS,UAAU,IAAK,UAAU;AAEpG,QAAM,SAAS,UAAU,UAAU,GAAG,SAAS,EAAE,KAAK,UAAU,GAAG,SAAS,EAAE;AAC9E,MAAI,WAAW,GAAG;AAChB,WAAO;AAAA,EACT,OACK;AACH,WAAO,SAAS,QAAQ,QAAQ,SAAS,CAAC;AAAA,EAC5C;AACF;;;AFrCA,aAAc,eAAc,IAAI,uBAAkB,OAAmB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple hash string for fast hashing (like md5).
|
|
3
|
+
* This function is not very secure and should not be used for security purposes.
|
|
4
|
+
* But it cannot be reversed easily and brute force can take up to years for fast computers.
|
|
5
|
+
*
|
|
6
|
+
* @param str - The string or number to hash
|
|
7
|
+
* @param prefix - A prefix to add to the beginning of the hash result
|
|
8
|
+
* @param repeat - Number of times to repeat the hashing process for increased complexity (default: 3)
|
|
9
|
+
* @returns A hashed string with the specified prefix
|
|
10
|
+
*/
|
|
11
|
+
export declare function nanoHash(str: string | number, prefix: string, repeat?: number): string;
|
|
12
|
+
//# sourceMappingURL=nano-hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nano-hash.d.ts","sourceRoot":"","sources":["../src/nano-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,MAAM,CA6BjF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alwatr/hash-string",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "A simple utility to generate a hash string.",
|
|
5
5
|
"author": "S. Ali Mihandoost <ali.mihandoost@gmail.com>",
|
|
6
6
|
"keywords": [
|
|
@@ -71,14 +71,14 @@
|
|
|
71
71
|
"clean": "rm -rfv dist *.tsbuildinfo"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@alwatr/package-tracer": "^5.5.
|
|
74
|
+
"@alwatr/package-tracer": "^5.5.2"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@alwatr/nano-build": "^5.5.
|
|
77
|
+
"@alwatr/nano-build": "^5.5.2",
|
|
78
78
|
"@alwatr/prettier-config": "^5.0.0",
|
|
79
79
|
"@alwatr/tsconfig-base": "^5.0.0",
|
|
80
80
|
"jest": "^29.7.0",
|
|
81
81
|
"typescript": "^5.8.2"
|
|
82
82
|
},
|
|
83
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "2756475ba2fa108bae7af6d8d9747e0c7815dfa2"
|
|
84
84
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {djb2Hash} from '@alwatr/hash-string';
|
|
2
|
+
|
|
3
|
+
describe('djb2Hash', () => {
|
|
4
|
+
it('should generate numeric hash values for string inputs', () => {
|
|
5
|
+
const result = djb2Hash('test');
|
|
6
|
+
expect(typeof result).toBe('number');
|
|
7
|
+
expect(Number.isInteger(result)).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should return different hashes for different inputs', () => {
|
|
11
|
+
const hash1 = djb2Hash('test1');
|
|
12
|
+
const hash2 = djb2Hash('test2');
|
|
13
|
+
expect(hash1).not.toBe(hash2);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should generate consistent hashes for the same input', () => {
|
|
17
|
+
const input = 'consistencyTest';
|
|
18
|
+
const hash1 = djb2Hash(input);
|
|
19
|
+
const hash2 = djb2Hash(input);
|
|
20
|
+
expect(hash1).toBe(hash2);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should handle empty strings', () => {
|
|
24
|
+
const result = djb2Hash('');
|
|
25
|
+
expect(typeof result).toBe('number');
|
|
26
|
+
// The hash for an empty string should be the initial value right-shifted by 0
|
|
27
|
+
expect(result).toBe(5381 >>> 0);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should handle special characters', () => {
|
|
31
|
+
const result = djb2Hash('!@#$%^&*()');
|
|
32
|
+
expect(typeof result).toBe('number');
|
|
33
|
+
expect(Number.isInteger(result)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should handle long strings', () => {
|
|
37
|
+
const longString = 'a'.repeat(1000);
|
|
38
|
+
const result = djb2Hash(longString);
|
|
39
|
+
expect(typeof result).toBe('number');
|
|
40
|
+
expect(Number.isInteger(result)).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should handle Unicode characters', () => {
|
|
44
|
+
const unicodeString = '😀🌍🚀';
|
|
45
|
+
const result = djb2Hash(unicodeString);
|
|
46
|
+
expect(typeof result).toBe('number');
|
|
47
|
+
expect(Number.isInteger(result)).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should produce 32-bit unsigned integer outputs', () => {
|
|
51
|
+
const inputs = ['test', 'hello world', 'unicode 😀', 'a'.repeat(1000)];
|
|
52
|
+
|
|
53
|
+
for (const input of inputs) {
|
|
54
|
+
const result = djb2Hash(input);
|
|
55
|
+
expect(result).toBeLessThanOrEqual(0xFFFFFFFF); // Max 32-bit unsigned int
|
|
56
|
+
expect(result).toBeGreaterThanOrEqual(0);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should produce deterministic results', () => {
|
|
61
|
+
// Test with known hash values
|
|
62
|
+
expect(djb2Hash('hello')).toBe(181380007);
|
|
63
|
+
expect(djb2Hash('world')).toBe(164394279);
|
|
64
|
+
expect(djb2Hash('hello world')).toBe(2616892229);
|
|
65
|
+
expect(djb2Hash('')).toBe(5381);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should handle case sensitivity', () => {
|
|
69
|
+
expect(djb2Hash('Hello')).not.toBe(djb2Hash('hello'));
|
|
70
|
+
expect(djb2Hash('WORLD')).not.toBe(djb2Hash('world'));
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {nanoHash} from '@alwatr/hash-string';
|
|
2
2
|
|
|
3
3
|
describe('hashString', () => {
|
|
4
4
|
it('should generate hash for string inputs', () => {
|
|
5
|
-
const result =
|
|
5
|
+
const result = nanoHash('test', 'prefix-');
|
|
6
6
|
expect(typeof result).toBe('string');
|
|
7
7
|
expect(result.startsWith('prefix-')).toBe(true);
|
|
8
8
|
expect(result).not.toBe('prefix-test');
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
it('should generate hash for numeric inputs', () => {
|
|
12
|
-
const result =
|
|
12
|
+
const result = nanoHash(12345, 'num-');
|
|
13
13
|
expect(typeof result).toBe('string');
|
|
14
14
|
expect(result.startsWith('num-')).toBe(true);
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
it('should return different hashes for different inputs', () => {
|
|
18
|
-
const hash1 =
|
|
19
|
-
const hash2 =
|
|
18
|
+
const hash1 = nanoHash('test1', 'p-');
|
|
19
|
+
const hash2 = nanoHash('test2', 'p-');
|
|
20
20
|
expect(hash1).not.toBe(hash2);
|
|
21
21
|
});
|
|
22
22
|
|
|
@@ -24,7 +24,7 @@ describe('hashString', () => {
|
|
|
24
24
|
const prefixes = ['a-', 'test-', 'hash_', '123-'];
|
|
25
25
|
|
|
26
26
|
prefixes.forEach((prefix) => {
|
|
27
|
-
const result =
|
|
27
|
+
const result = nanoHash('sameInput', prefix);
|
|
28
28
|
expect(result.startsWith(prefix)).toBe(true);
|
|
29
29
|
});
|
|
30
30
|
});
|
|
@@ -33,9 +33,9 @@ describe('hashString', () => {
|
|
|
33
33
|
const input = 'repeatTest';
|
|
34
34
|
const prefix = 'r-';
|
|
35
35
|
|
|
36
|
-
const hash1 =
|
|
37
|
-
const hash2 =
|
|
38
|
-
const hash3 =
|
|
36
|
+
const hash1 = nanoHash(input, prefix, 1);
|
|
37
|
+
const hash2 = nanoHash(input, prefix, 2);
|
|
38
|
+
const hash3 = nanoHash(input, prefix, 3);
|
|
39
39
|
|
|
40
40
|
expect(hash1).not.toBe(hash2);
|
|
41
41
|
expect(hash2).not.toBe(hash3);
|
|
@@ -43,14 +43,14 @@ describe('hashString', () => {
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
it('should handle empty strings', () => {
|
|
46
|
-
const result =
|
|
46
|
+
const result = nanoHash('', 'empty-');
|
|
47
47
|
expect(typeof result).toBe('string');
|
|
48
48
|
expect(result.startsWith('empty-')).toBe(true);
|
|
49
49
|
expect(result.length).toBeGreaterThan('empty-'.length);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
it('should handle special characters', () => {
|
|
53
|
-
const result =
|
|
53
|
+
const result = nanoHash('!@#$%^&*()', 'special-');
|
|
54
54
|
expect(typeof result).toBe('string');
|
|
55
55
|
expect(result.startsWith('special-')).toBe(true);
|
|
56
56
|
});
|
|
@@ -59,15 +59,15 @@ describe('hashString', () => {
|
|
|
59
59
|
const input = 'consistencyTest';
|
|
60
60
|
const prefix = 'c-';
|
|
61
61
|
|
|
62
|
-
const hash1 =
|
|
63
|
-
const hash2 =
|
|
62
|
+
const hash1 = nanoHash(input, prefix);
|
|
63
|
+
const hash2 = nanoHash(input, prefix);
|
|
64
64
|
|
|
65
65
|
expect(hash1).toBe(hash2);
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
it('should handle long strings', () => {
|
|
69
69
|
const longString = 'a'.repeat(1000);
|
|
70
|
-
const result =
|
|
70
|
+
const result = nanoHash(longString, 'long-');
|
|
71
71
|
|
|
72
72
|
expect(typeof result).toBe('string');
|
|
73
73
|
expect(result.startsWith('long-')).toBe(true);
|
|
@@ -75,19 +75,27 @@ describe('hashString', () => {
|
|
|
75
75
|
|
|
76
76
|
it('should handle Unicode characters', () => {
|
|
77
77
|
const unicodeString = '😀🌍🚀';
|
|
78
|
-
const result =
|
|
78
|
+
const result = nanoHash(unicodeString, 'unicode-');
|
|
79
79
|
|
|
80
80
|
expect(typeof result).toBe('string');
|
|
81
81
|
expect(result.startsWith('unicode-')).toBe(true);
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
it('should default to repeat value of
|
|
84
|
+
it('should default to repeat value of 1 when not specified', () => {
|
|
85
85
|
const input = 'defaultRepeat';
|
|
86
86
|
const prefix = 'd-';
|
|
87
87
|
|
|
88
|
-
const defaultResult =
|
|
89
|
-
const explicitResult =
|
|
88
|
+
const defaultResult = nanoHash(input, prefix);
|
|
89
|
+
const explicitResult = nanoHash(input, prefix, 1);
|
|
90
90
|
|
|
91
91
|
expect(defaultResult).toBe(explicitResult);
|
|
92
92
|
});
|
|
93
|
+
|
|
94
|
+
it('should algorithm be deterministic', () => {
|
|
95
|
+
const input = 'Ali@MD_65';
|
|
96
|
+
const prefix = 'p-';
|
|
97
|
+
const expectedHash = 'p-dm3wzfp6jiud';
|
|
98
|
+
const hash = nanoHash(input, prefix, 3);
|
|
99
|
+
expect(hash).toBe(expectedHash);
|
|
100
|
+
});
|
|
93
101
|
});
|