Skip to content

Commit d91293a

Browse files
committed
feat(xxhash): implement xxh3
1 parent 0af0a29 commit d91293a

File tree

5 files changed

+267
-2
lines changed

5 files changed

+267
-2
lines changed

packages/xxhash/README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
> 🚀 Help me to become a full-time open-source developer by [sponsoring me on Github](https://github.com/sponsors/Brooooooklyn)
88
9-
Fastest `xxhash` implementation in Node.js.
9+
[`xxhash-rust`](https://github.com/DoumanAsh/xxhash-rust) binding for Node.js.
1010

1111
## Install this package
1212

@@ -52,12 +52,31 @@ export class Xxh32 {
5252
constructor(seed?: number)
5353
update(input: BufferLike): this
5454
digest(): number
55+
reset(): void
5556
}
5657

5758
export class Xxh64 {
5859
constructor(seed?: BigInt)
5960
update(input: BufferLike): this
6061
digest(): BigInt
62+
reset(): void
63+
}
64+
65+
export class Xxh3 {
66+
static withSeed(seed?: BigInt): Xxh3
67+
static withSecret(secret: BufferLike): Xxh3
68+
private constructor() {}
69+
update(input: BufferLike): this
70+
digest(): BigInt
71+
reset(): void
72+
}
73+
74+
export const xxh3: {
75+
xxh64: (input: BufferLike, seed?: BigInt) => BigInt
76+
xxh64WithSecret: (input: BufferLike, secret: BufferLike) => BigInt
77+
xxh128: (input: BufferLike, seed?: BigInt) => BigInt
78+
xxh128WithSecret: (input: BufferLike, secret: BufferLike) => BigInt
79+
Xxh3: typeof Xxh3
6180
}
6281
```
6382
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import test from 'ava'
2+
3+
import { xxh3 } from '../index'
4+
5+
const SEC_WITH_192_LENGTH = Buffer.from(
6+
'515293b422141cabb24c131a914d54d767738ce3f46141d91dfdfffa8b2e7ada507318f242dd112f28f213cfc1c4aba1e641e8a7f103746cc542d66668607e2ea4fce4f08640780d0bcd9f171a31f8932ae617033afd5e100c3fb6f0b5b9be611419d79c2bf2358ba1c8562ae24dd1aa2619ab30dcfaa9b8f3363b2a350e750a6aae7e307d16b1d3250f7ed6315ec127fac8643dfcb733ffe622bbc97a3097c6eabd24dee519bc7817e0e8195a426b07ad7452f6ee72465e065afe56e498a450',
7+
'hex',
8+
)
9+
10+
test('xxh64 string', (t) => {
11+
t.is(xxh3.xxh64('hello world'), BigInt('15296390279056496779'))
12+
})
13+
14+
test('xxh64 Buffer', (t) => {
15+
t.is(xxh3.xxh64(Buffer.from('hello world')), BigInt('15296390279056496779'))
16+
})
17+
18+
test('xxh64 with seed', (t) => {
19+
t.is(xxh3.xxh64(Buffer.from('hello world'), BigInt(128)), BigInt('18072542215751182891'))
20+
})
21+
22+
test('xxh64 with secret', (t) => {
23+
t.is(xxh3.xxh64WithSecret('hello world', SEC_WITH_192_LENGTH), BigInt('8365614992180151249'))
24+
})
25+
26+
test('xxh128 string', (t) => {
27+
t.is(xxh3.xxh128('hello world'), BigInt('297150157938599054391163723952090887879'))
28+
})
29+
30+
test('xxh128 buffer', (t) => {
31+
t.is(xxh3.xxh128(Buffer.from('hello world')), BigInt('297150157938599054391163723952090887879'))
32+
})
33+
34+
test('xxh128 with seed', (t) => {
35+
t.is(xxh3.xxh128(Buffer.from('hello world'), BigInt(128)), BigInt('248039115514001876413444952452915338056'))
36+
})
37+
38+
test('xxh128 with secret', (t) => {
39+
t.is(xxh3.xxh128WithSecret('hello world', SEC_WITH_192_LENGTH), BigInt('169165111715981571090973585540606896681'))
40+
})
41+
42+
test('Xxh3 withSeed', (t) => {
43+
const instance = xxh3.Xxh3.withSeed()
44+
t.true(instance instanceof xxh3.Xxh3)
45+
t.is(instance.update('hello world').digest(), BigInt('15296390279056496779'))
46+
t.is(instance.update(Buffer.from('hello world')).digest(), BigInt('16495854690286049632'))
47+
instance.reset()
48+
t.is(instance.update('hello world').digest(), BigInt('15296390279056496779'))
49+
})
50+
51+
test('Xxh3 withSecret', (t) => {
52+
const instance = xxh3.Xxh3.withSecret(SEC_WITH_192_LENGTH)
53+
t.true(instance instanceof xxh3.Xxh3)
54+
t.is(instance.update('hello world').digest(), BigInt('8365614992180151249'))
55+
t.is(instance.update(Buffer.from('hello world')).digest(), BigInt('14168446104542996972'))
56+
instance.reset()
57+
t.is(instance.update('hello world').digest(), BigInt('8365614992180151249'))
58+
})

packages/xxhash/index.d.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,29 @@ export class Xxh32 {
1414
constructor(seed?: number)
1515
update(input: BufferLike): this
1616
digest(): number
17+
reset(): void
1718
}
1819

1920
export class Xxh64 {
2021
constructor(seed?: BigInt)
2122
update(input: BufferLike): this
2223
digest(): BigInt
24+
reset(): void
25+
}
26+
27+
export class Xxh3 {
28+
static withSeed(seed?: BigInt): Xxh3
29+
static withSecret(secret: BufferLike): Xxh3
30+
private constructor() {}
31+
update(input: BufferLike): this
32+
digest(): BigInt
33+
reset(): void
34+
}
35+
36+
export const xxh3: {
37+
xxh64: (input: BufferLike, seed?: BigInt) => BigInt
38+
xxh64WithSecret: (input: BufferLike, secret: BufferLike) => BigInt
39+
xxh128: (input: BufferLike, seed?: BigInt) => BigInt
40+
xxh128WithSecret: (input: BufferLike, secret: BufferLike) => BigInt
41+
Xxh3: typeof Xxh3
2342
}

packages/xxhash/index.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,30 @@ const {
55
xxh64: _xxh64,
66
Xxh32: _Xxh32,
77
Xxh64: _Xxh64,
8+
xxh3,
89
} = loadBinding(__dirname, 'xxhash', '@node-rs/xxhash')
910

11+
class Xxh3 {
12+
update(data) {
13+
return xxh3.update.call(this, Buffer.from(data))
14+
}
15+
}
16+
17+
Xxh3.withSecret = function withSecret(secret) {
18+
const instance = new Xxh3()
19+
xxh3.createXxh3WithSecret(instance, Buffer.from(secret))
20+
return instance
21+
}
22+
23+
Xxh3.withSeed = function withSeed(seed = BigInt(0)) {
24+
const instance = new Xxh3()
25+
xxh3.createXxh3WithSeed(instance, seed)
26+
return instance
27+
}
28+
29+
Xxh3.prototype.digest = xxh3.digest
30+
Xxh3.prototype.reset = xxh3.reset
31+
1032
module.exports = {
1133
xxh32: function xxh32(input, seed) {
1234
return _xxh32(Buffer.from(input), seed == null ? 0 : seed)
@@ -24,4 +46,19 @@ module.exports = {
2446
return super.update(Buffer.from(input))
2547
}
2648
},
49+
xxh3: {
50+
xxh64: function xxh64(input, seed) {
51+
return xxh3.xxh64(Buffer.from(input), seed == null ? BigInt(0) : seed)
52+
},
53+
xxh64WithSecret(input, secret) {
54+
return xxh3.xxh64WithSecret(Buffer.from(input), Buffer.from(secret))
55+
},
56+
xxh128: function xxh128(input, seed) {
57+
return xxh3.xxh128(Buffer.from(input), seed == null ? BigInt(0) : seed)
58+
},
59+
xxh128WithSecret(input, secret) {
60+
return xxh3.xxh128WithSecret(Buffer.from(input), Buffer.from(secret))
61+
},
62+
Xxh3,
63+
},
2764
}

packages/xxhash/src/lib.rs

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ extern crate global_alloc;
33

44
use napi::*;
55
use napi_derive::*;
6-
use xxhash_rust::{xxh32, xxh64};
6+
use xxhash_rust::{xxh3, xxh32, xxh64};
77

88
#[module_exports]
99
fn init(mut exports: JsObject, env: Env) -> Result<()> {
@@ -16,6 +16,7 @@ fn init(mut exports: JsObject, env: Env) -> Result<()> {
1616
&[
1717
Property::new(&env, "update")?.with_method(update_xxh32),
1818
Property::new(&env, "digest")?.with_method(digest_xxh32),
19+
Property::new(&env, "reset")?.with_method(reset_xxh32),
1920
],
2021
)?;
2122
let xxh64_class = env.define_class(
@@ -24,10 +25,23 @@ fn init(mut exports: JsObject, env: Env) -> Result<()> {
2425
&[
2526
Property::new(&env, "update")?.with_method(update_xxh64),
2627
Property::new(&env, "digest")?.with_method(digest_xxh64),
28+
Property::new(&env, "reset")?.with_method(reset_xxh64),
2729
],
2830
)?;
2931
exports.set_named_property("Xxh32", xxh32_class)?;
3032
exports.set_named_property("Xxh64", xxh64_class)?;
33+
34+
let mut xxh3 = env.create_object()?;
35+
xxh3.create_named_method("xxh64", xxh3_xxh64)?;
36+
xxh3.create_named_method("xxh64WithSecret", xxh3_xxh64_with_secret)?;
37+
xxh3.create_named_method("xxh128", xxh3_xxh128)?;
38+
xxh3.create_named_method("xxh128WithSecret", xxh3_xxh128_with_secret)?;
39+
xxh3.create_named_method("createXxh3WithSeed", create_xxh3_with_seed)?;
40+
xxh3.create_named_method("createXxh3WithSecret", create_xxh3_with_secret)?;
41+
xxh3.create_named_method("update", update_xxh3)?;
42+
xxh3.create_named_method("digest", digest_xxh3)?;
43+
xxh3.create_named_method("reset", reset_xxh3)?;
44+
exports.set_named_property("xxh3", xxh3)?;
3145
Ok(())
3246
}
3347

@@ -69,6 +83,19 @@ fn digest_xxh32(ctx: CallContext) -> Result<JsNumber> {
6983
ctx.env.create_uint32(native.digest())
7084
}
7185

86+
#[js_function(1)]
87+
fn reset_xxh32(ctx: CallContext) -> Result<JsUndefined> {
88+
let this = ctx.this_unchecked::<JsObject>();
89+
let native = ctx.env.unwrap::<xxh32::Xxh32>(&this)?;
90+
let seed = if ctx.length == 1 {
91+
ctx.get::<JsNumber>(0)?.get_uint32()?
92+
} else {
93+
0
94+
};
95+
native.reset(seed);
96+
ctx.env.get_undefined()
97+
}
98+
7299
#[js_function(2)]
73100
fn xxh64(ctx: CallContext) -> Result<JsBigint> {
74101
let input = ctx.get::<JsBuffer>(0)?.into_value()?;
@@ -106,3 +133,108 @@ fn digest_xxh64(ctx: CallContext) -> Result<JsBigint> {
106133
let native = ctx.env.unwrap::<xxh64::Xxh64>(&this)?;
107134
ctx.env.create_bigint_from_u64(native.digest())
108135
}
136+
137+
#[js_function(1)]
138+
fn reset_xxh64(ctx: CallContext) -> Result<JsUndefined> {
139+
let this = ctx.this_unchecked::<JsObject>();
140+
let native = ctx.env.unwrap::<xxh64::Xxh64>(&this)?;
141+
let seed = if ctx.length == 1 {
142+
ctx.get::<JsBigint>(0)?.get_u64()?.0
143+
} else {
144+
0
145+
};
146+
native.reset(seed);
147+
ctx.env.get_undefined()
148+
}
149+
150+
#[js_function(2)]
151+
fn xxh3_xxh64(ctx: CallContext) -> Result<JsBigint> {
152+
let input = ctx.get::<JsBuffer>(0)?.into_value()?;
153+
let seed = if ctx.length == 2 {
154+
ctx.get::<JsBigint>(1)?.get_u64()?.0
155+
} else {
156+
0
157+
};
158+
ctx
159+
.env
160+
.create_bigint_from_u64(xxh3::xxh3_64_with_seed(input.as_ref(), seed))
161+
}
162+
163+
#[js_function(2)]
164+
fn xxh3_xxh64_with_secret(ctx: CallContext) -> Result<JsBigint> {
165+
let input = ctx.get::<JsBuffer>(0)?.into_value()?;
166+
let secret = ctx.get::<JsBuffer>(1)?.into_value()?;
167+
ctx
168+
.env
169+
.create_bigint_from_u64(xxh3::xxh3_64_with_secret(input.as_ref(), secret.as_ref()))
170+
}
171+
172+
#[js_function(2)]
173+
fn xxh3_xxh128(ctx: CallContext) -> Result<JsBigint> {
174+
let input = ctx.get::<JsBuffer>(0)?.into_value()?;
175+
let seed = if ctx.length == 2 {
176+
ctx.get::<JsBigint>(1)?.get_u64()?.0
177+
} else {
178+
0
179+
};
180+
ctx
181+
.env
182+
.create_bigint_from_u128(xxh3::xxh3_128_with_seed(input.as_ref(), seed))
183+
}
184+
185+
#[js_function(2)]
186+
fn xxh3_xxh128_with_secret(ctx: CallContext) -> Result<JsBigint> {
187+
let input = ctx.get::<JsBuffer>(0)?.into_value()?;
188+
let secret = ctx.get::<JsBuffer>(1)?.into_value()?;
189+
ctx
190+
.env
191+
.create_bigint_from_u128(xxh3::xxh3_128_with_secret(input.as_ref(), secret.as_ref()))
192+
}
193+
194+
#[js_function(2)]
195+
fn create_xxh3_with_seed(ctx: CallContext) -> Result<JsUndefined> {
196+
let mut js_this = ctx.get::<JsObject>(0)?;
197+
let seed = if ctx.length == 2 {
198+
ctx.get::<JsBigint>(1)?.get_u64()?.0
199+
} else {
200+
0
201+
};
202+
let xxh3_instance = xxh3::Xxh3::with_seed(seed);
203+
ctx.env.wrap(&mut js_this, xxh3_instance)?;
204+
ctx.env.get_undefined()
205+
}
206+
207+
#[js_function(2)]
208+
fn create_xxh3_with_secret(ctx: CallContext) -> Result<JsUndefined> {
209+
let mut js_this = ctx.get::<JsObject>(0)?;
210+
let secret = ctx.get::<JsBuffer>(1)?.into_value()?;
211+
let mut sec = [0u8; 192];
212+
sec.copy_from_slice(secret.as_ref());
213+
let xxh3_instance = xxh3::Xxh3::with_secret(sec);
214+
ctx.env.wrap(&mut js_this, xxh3_instance)?;
215+
ctx.env.get_undefined()
216+
}
217+
218+
#[js_function(2)]
219+
fn update_xxh3(ctx: CallContext) -> Result<JsObject> {
220+
let this = ctx.this_unchecked::<JsObject>();
221+
let native = ctx.env.unwrap::<xxh3::Xxh3>(&this)?;
222+
let input = ctx.get::<JsBuffer>(0)?.into_value()?;
223+
native.update(input.as_ref());
224+
Ok(this)
225+
}
226+
227+
#[js_function]
228+
fn digest_xxh3(ctx: CallContext) -> Result<JsBigint> {
229+
let this = ctx.this_unchecked::<JsObject>();
230+
let native = ctx.env.unwrap::<xxh3::Xxh3>(&this)?;
231+
ctx.env.create_bigint_from_u64(native.digest())
232+
}
233+
234+
#[js_function]
235+
fn reset_xxh3(ctx: CallContext) -> Result<JsUndefined> {
236+
let this = ctx.this_unchecked::<JsObject>();
237+
let native = ctx.env.unwrap::<xxh3::Xxh3>(&this)?;
238+
native.reset();
239+
ctx.env.get_undefined()
240+
}

0 commit comments

Comments
 (0)