Skip to content

Commit fea7f19

Browse files
committed
chore: update dependencies and improve HashedSet implementation
1 parent b3ec0d3 commit fea7f19

File tree

9 files changed

+91
-276
lines changed

9 files changed

+91
-276
lines changed

libs/hashed-set/README.md

Lines changed: 0 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1 @@
11
# @remnawave/hashed-set
2-
3-
A high-performance Set implementation that maintains a hash value for fast equality comparison. This allows for O(1) set comparison operations instead of O(n) when comparing two sets.
4-
5-
## Features
6-
7-
- **Fast Set Comparison**: Compare sets in O(1) time using hash values
8-
- **TypeScript Support**: Full TypeScript definitions included
9-
- **Standard Set API**: Extends native JavaScript Set with additional methods
10-
- **Memory Efficient**: Minimal overhead compared to standard Set
11-
- **Multiple Data Types**: Supports numbers, strings, and objects
12-
13-
## Installation
14-
15-
```bash
16-
npm install @remnawave/hashed-set
17-
```
18-
19-
## Usage
20-
21-
### Basic Usage
22-
23-
```typescript
24-
import { HashedSet } from '@remnawave/hashed-set';
25-
26-
// Create a new HashedSet
27-
const set1 = new HashedSet([1, 2, 3]);
28-
const set2 = new HashedSet([3, 2, 1]);
29-
30-
// Fast comparison using hash values
31-
console.log(set1.hash === set2.hash); // true - same elements, same hash
32-
33-
// Standard Set operations work as expected
34-
set1.add(4);
35-
set1.delete(1);
36-
console.log(set1.has(2)); // true
37-
console.log(set1.size); // 3
38-
```
39-
40-
### Advanced Features
41-
42-
```typescript
43-
import { HashedSet } from '@remnawave/hashed-set';
44-
45-
const set1 = new HashedSet(['alice@example.com', 'bob@example.com']);
46-
const set2 = new HashedSet(['bob@example.com', 'alice@example.com']);
47-
48-
// Quick hash comparison
49-
if (set1.hasSameHash(set2)) {
50-
console.log('Sets likely contain the same elements');
51-
}
52-
53-
// Get unique hash for the set
54-
console.log(`Set hash: ${set1.hash}`);
55-
```
56-
57-
### Performance Example
58-
59-
```typescript
60-
import { HashedSet } from '@remnawave/hashed-set';
61-
62-
// Create large sets
63-
const emails1 = Array.from({ length: 100000 }, (_, i) => `user${i}@example.com`);
64-
const emails2 = [...emails1].reverse();
65-
66-
const set1 = new HashedSet(emails1);
67-
const set2 = new HashedSet(emails2);
68-
69-
// O(1) comparison instead of O(n)
70-
const startTime = performance.now();
71-
const areEqual = set1.hash === set2.hash;
72-
const endTime = performance.now();
73-
74-
console.log(`Comparison took ${endTime - startTime}ms`); // ~0.01ms instead of ~100ms
75-
```
76-
77-
## API Reference
78-
79-
### Constructor
80-
81-
- `new HashedSet<T>(iterable?: Iterable<T>)` - Creates a new HashedSet
82-
83-
### Properties
84-
85-
- `hash: number` - Gets the current hash value of the set (unsigned 32-bit integer)
86-
- `size: number` - Number of elements in the set (inherited from Set)
87-
88-
### Methods
89-
90-
#### Standard Set Methods
91-
92-
- `add(value: T): this` - Adds a value to the set
93-
- `delete(value: T): boolean` - Removes a value from the set
94-
- `has(value: T): boolean` - Checks if a value exists in the set
95-
- `clear(): void` - Removes all values from the set
96-
- `forEach()`, `values()`, `keys()`, `entries()` - Standard iteration methods
97-
98-
#### Additional Methods
99-
100-
- `hasSameHash(other: HashedSet<T>): boolean` - Fast hash-based comparison
101-
102-
## Hash Algorithm
103-
104-
The library uses a combination of:
105-
106-
- **Numbers**: Direct integer conversion
107-
- **Strings**: 31-polynomial rolling hash
108-
- **Objects**: Random unique identifier assignment
109-
110-
The final set hash is computed using XOR operations, making it order-independent.
111-
112-
## Performance Characteristics
113-
114-
- **Set Creation**: O(n) where n is the number of elements
115-
- **Add/Delete**: O(1) average case
116-
- **Hash Comparison**: O(1)
117-
118-
## License
119-
120-
AGPL-3.0-only

libs/hashed-set/examples/basic-usage.ts

Lines changed: 0 additions & 52 deletions
This file was deleted.

libs/hashed-set/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@remnawave/hashed-set",
3-
"version": "0.0.2",
3+
"version": "0.0.4",
44
"public": true,
55
"license": "AGPL-3.0-only",
66
"description": "A high-performance Set implementation with hash-based equality comparison for fast set operations.",

libs/hashed-set/src/hashed-set.ts

Lines changed: 75 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,97 @@
1-
/**
2-
* A high-performance Set implementation that maintains a hash value for fast equality comparison.
3-
* This allows for O(1) set comparison operations instead of O(n) when comparing two sets.
4-
*
5-
* @example
6-
* ```typescript
7-
* const set1 = new HashedSet([1, 2, 3]);
8-
* const set2 = new HashedSet([3, 2, 1]);
9-
*
10-
* // Fast comparison using hash values
11-
* console.log(set1.hash === set2.hash); // true - same elements, same hash
12-
*
13-
* // Standard Set operations work as expected
14-
* set1.add(4);
15-
* set1.delete(1);
16-
* console.log(set1.has(2)); // true
17-
* ```
18-
*/
19-
export class HashedSet<T = any> extends Set<T> {
20-
private _hash: number;
1+
export class HashedSet extends Set<string> {
2+
private _hashHigh: number = 0;
3+
private _hashLow: number = 0;
214

22-
/**
23-
* Creates a new HashedSet instance.
24-
* @param iterable Optional iterable to initialize the set with
25-
*/
26-
constructor(iterable?: Iterable<T>) {
27-
super(iterable ? Array.from(iterable) : undefined);
28-
this._hash = 0;
29-
for (const v of this) {
30-
this._hash ^= this._valHash(v);
5+
constructor(iterable?: Iterable<string>) {
6+
super(iterable);
7+
for (const str of this) {
8+
const hash64 = this._djb2Dual(str);
9+
this._hashHigh ^= hash64.high;
10+
this._hashLow ^= hash64.low;
3111
}
3212
}
3313

34-
/**
35-
* Computes a hash value for a given value.
36-
* @param v The value to hash
37-
* @returns The computed hash value
38-
*/
39-
private _valHash(v: T): number {
40-
if (typeof v === 'number') return v | 0;
41-
if (typeof v === 'string') {
42-
let h = 0;
43-
for (let i = 0; i < v.length; i++) {
44-
h = (h * 31 + v.charCodeAt(i)) | 0;
45-
}
46-
return h;
14+
private _djb2Dual(str: string): { high: number; low: number } {
15+
let high = 5381;
16+
let low = 5387;
17+
18+
for (let i = 0; i < str.length; i++) {
19+
const char = str.charCodeAt(i);
20+
21+
high = ((high << 5) + high + char) | 0;
22+
low = ((low << 6) + low + char * 37) | 0;
4723
}
48-
return Object.is(v, null) ? 0 : this._objHash(v);
49-
}
5024

51-
/**
52-
* Computes a hash value for an object by assigning a unique identifier.
53-
* @param obj The object to hash
54-
* @returns The computed hash value
55-
*/
56-
private _objHash(obj: any): number {
57-
return (obj.__hid ??= (Math.random() * 0xffffffff) | 0);
25+
return {
26+
high: high >>> 0,
27+
low: low >>> 0,
28+
};
5829
}
5930

60-
/**
61-
* Adds a value to the set and updates the hash.
62-
* @param v The value to add
63-
* @returns The HashedSet instance
64-
*/
65-
add(v: T): this {
66-
if (!this.has(v)) {
67-
this._hash ^= this._valHash(v);
31+
add(str: string): this {
32+
if (!this.has(str)) {
33+
const hash64 = this._djb2Dual(str);
34+
this._hashHigh ^= hash64.high;
35+
this._hashLow ^= hash64.low;
6836
}
69-
return super.add(v);
37+
return super.add(str);
7038
}
7139

72-
/**
73-
* Removes a value from the set and updates the hash.
74-
* @param v The value to remove
75-
* @returns True if the value was removed, false otherwise
76-
*/
77-
delete(v: T): boolean {
78-
if (this.has(v)) {
79-
this._hash ^= this._valHash(v);
40+
delete(str: string): boolean {
41+
if (this.has(str)) {
42+
const hash64 = this._djb2Dual(str);
43+
this._hashHigh ^= hash64.high;
44+
this._hashLow ^= hash64.low;
8045
}
81-
return super.delete(v);
46+
return super.delete(str);
8247
}
8348

84-
/**
85-
* Clears all values from the set and resets the hash.
86-
*/
8749
clear(): void {
88-
this._hash = 0;
8950
super.clear();
51+
this._hashHigh = 0;
52+
this._hashLow = 0;
53+
}
54+
55+
get hashHigh(): number {
56+
return this._hashHigh;
57+
}
58+
get hashLow(): number {
59+
return this._hashLow;
9060
}
9161

92-
/**
93-
* Gets the current hash value of the set.
94-
* Two sets with the same elements will have the same hash value.
95-
* @returns The hash value as an unsigned 32-bit integer
96-
*/
97-
get hash(): number {
98-
return this._hash >>> 0;
62+
hasSameHash(other: HashedSet): boolean {
63+
return this.hashHigh === other.hashHigh && this.hashLow === other.hashLow;
9964
}
10065

101-
/**
102-
* Checks if this set has the same hash as another HashedSet.
103-
* This is a fast way to check if two sets might be equal.
104-
* @param other The other HashedSet to compare with
105-
* @returns True if the hash values are equal
106-
*/
107-
hasSameHash(other: HashedSet<T>): boolean {
108-
return this.hash === other.hash;
66+
get hash64String(): string {
67+
return (
68+
this.hashHigh.toString(16).padStart(8, '0') + this.hashLow.toString(16).padStart(8, '0')
69+
);
70+
}
71+
72+
fastEquals(other: HashedSet): boolean {
73+
return this.size === other.size && this.hasSameHash(other);
74+
}
75+
76+
getHashStats(): {
77+
size: number;
78+
hashHigh: string;
79+
hashLow: string;
80+
hash64: string;
81+
entropy: number;
82+
} {
83+
let setBits = 0;
84+
for (let i = 0; i < 32; i++) {
85+
if (this._hashHigh & (1 << i)) setBits++;
86+
if (this._hashLow & (1 << i)) setBits++;
87+
}
88+
89+
return {
90+
size: this.size,
91+
hashHigh: this._hashHigh.toString(16).padStart(8, '0'),
92+
hashLow: this._hashLow.toString(16).padStart(8, '0'),
93+
hash64: this.hash64String,
94+
entropy: setBits / 64,
95+
};
10996
}
11097
}

0 commit comments

Comments
 (0)