Skip to content

Commit f5e453a

Browse files
committed
feat: add async and no GIL functions
1 parent 618a094 commit f5e453a

File tree

3 files changed

+363
-9
lines changed

3 files changed

+363
-9
lines changed

py-gxhash/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ crate-type = ["cdylib"]
1010
[dependencies]
1111
pyo3 = "0.22.0"
1212
gxhash = { path = "..", features = ["hybrid"] }
13+
pyo3-async-runtimes = { version = "0.22.0", features = ["tokio-runtime"] }
14+
tokio = "1.41.1"

py-gxhash/gxhash.pyi

Lines changed: 289 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,289 @@
1-
def gxhash32(input: bytes, seed: int) -> int: ...
2-
def gxhash64(input: bytes, seed: int) -> int: ...
3-
def gxhash128(input: bytes, seed: int) -> int: ...
1+
def gxhash32(input_bytes: bytes, seed: int) -> int:
2+
"""
3+
Summary
4+
-------
5+
hashes an arbitrary stream of bytes to an u32
6+
7+
8+
Parameters
9+
----------
10+
input_bytes (bytes): input bytes to hash
11+
12+
seed (int): seed for the hash function
13+
14+
15+
Returns
16+
-------
17+
hash (int): u32 hash of the input bytes
18+
19+
20+
Example
21+
-------
22+
```python
23+
import gxhash
24+
25+
input_bytes = bytes([42] * 1000)
26+
seed = 1234
27+
print(f"Hash is {gxhash.gxhash32(input_bytes, seed)}!")
28+
```
29+
"""
30+
31+
32+
def gxhash32_nogil(input_bytes: bytes, seed: int) -> int:
33+
"""
34+
Summary
35+
-------
36+
hashes an arbitrary stream of bytes to an u32 without the GIL
37+
38+
39+
Parameters
40+
----------
41+
input_bytes (bytes): input bytes to hash
42+
43+
seed (int): seed for the hash function
44+
45+
46+
Returns
47+
-------
48+
hash (int): u32 hash of the input bytes
49+
50+
51+
Example
52+
-------
53+
```python
54+
import gxhash
55+
56+
input_bytes = bytes([42] * 1000)
57+
seed = 1234
58+
print(f"Hash is {gxhash.gxhash32_nogil(input_bytes, seed)}!")
59+
```
60+
"""
61+
62+
63+
async def gxhash32_async(input_bytes: bytes, seed: int) -> int:
64+
"""
65+
Summary
66+
-------
67+
hashes an arbitrary stream of bytes to an u32 asynchronously in a Rust thread
68+
69+
70+
Parameters
71+
----------
72+
input_bytes (bytes): input bytes to hash
73+
74+
seed (int): seed for the hash function
75+
76+
77+
Returns
78+
-------
79+
hash (int): u32 hash of the input bytes
80+
81+
82+
Example
83+
-------
84+
```python
85+
import gxhash
86+
import asyncio
87+
88+
async def main():
89+
input_bytes = bytes([42] * 1000)
90+
seed = 1234
91+
print(f"Hash is {await gxhash.gxhash32_async(input_bytes, seed)}!")
92+
93+
asyncio.run(main())
94+
```
95+
"""
96+
97+
98+
def gxhash64(input_bytes: bytes, seed: int) -> int:
99+
"""
100+
Summary
101+
-------
102+
hashes an arbitrary stream of bytes to an u64
103+
104+
105+
Parameters
106+
----------
107+
input_bytes (bytes): input bytes to hash
108+
109+
seed (int): seed for the hash function
110+
111+
112+
Returns
113+
-------
114+
hash (int): u64 hash of the input bytes
115+
116+
117+
Example
118+
-------
119+
```python
120+
import gxhash
121+
122+
input_bytes = bytes([42] * 1000)
123+
seed = 1234
124+
print(f"Hash is {gxhash.gxhash64(input_bytes, seed)}!")
125+
```
126+
"""
127+
128+
129+
def gxhash64_nogil(input_bytes: bytes, seed: int) -> int:
130+
"""
131+
Summary
132+
-------
133+
hashes an arbitrary stream of bytes to an u64 without the GIL
134+
135+
136+
Parameters
137+
----------
138+
input_bytes (bytes): input bytes to hash
139+
140+
seed (int): seed for the hash function
141+
142+
143+
Returns
144+
-------
145+
hash (int): u64 hash of the input bytes
146+
147+
148+
Example
149+
-------
150+
```python
151+
import gxhash
152+
153+
input_bytes = bytes([42] * 1000)
154+
seed = 1234
155+
print(f"Hash is {gxhash.gxhash64_nogil(input_bytes, seed)}!")
156+
```
157+
"""
158+
159+
160+
async def gxhash64_async(input_bytes: bytes, seed: int) -> int:
161+
"""
162+
Summary
163+
-------
164+
hashes an arbitrary stream of bytes to an u64 asynchronously in a Rust thread
165+
166+
167+
Parameters
168+
----------
169+
input_bytes (bytes): input bytes to hash
170+
171+
seed (int): seed for the hash function
172+
173+
174+
Returns
175+
-------
176+
hash (int): u64 hash of the input bytes
177+
178+
179+
Example
180+
-------
181+
```python
182+
import gxhash
183+
import asyncio
184+
185+
async def main():
186+
input_bytes = bytes([42] * 1000)
187+
seed = 1234
188+
print(f"Hash is {await gxhash.gxhash64_async(input_bytes, seed)}!")
189+
190+
asyncio.run(main())
191+
```
192+
"""
193+
194+
195+
def gxhash128(input_bytes: bytes, seed: int) -> int:
196+
"""
197+
Summary
198+
-------
199+
hashes an arbitrary stream of bytes to an u128
200+
201+
202+
Parameters
203+
----------
204+
input_bytes (bytes): input bytes to hash
205+
206+
seed (int): seed for the hash function
207+
208+
209+
Returns
210+
-------
211+
hash (int): u128 hash of the input bytes
212+
213+
214+
Example
215+
-------
216+
```python
217+
import gxhash
218+
219+
input_bytes = bytes([42] * 1000)
220+
seed = 1234
221+
print(f"Hash is {gxhash.gxhash128(input_bytes, seed)}!")
222+
```
223+
"""
224+
225+
226+
def gxhash128_nogil(input_bytes: bytes, seed: int) -> int:
227+
"""
228+
Summary
229+
-------
230+
hashes an arbitrary stream of bytes to an u128 without the GIL
231+
232+
233+
Parameters
234+
----------
235+
input_bytes (bytes): input bytes to hash
236+
237+
seed (int): seed for the hash function
238+
239+
240+
Returns
241+
-------
242+
hash (int): u128 hash of the input bytes
243+
244+
245+
Example
246+
-------
247+
```python
248+
import gxhash
249+
250+
input_bytes = bytes([42] * 1000)
251+
seed = 1234
252+
print(f"Hash is {gxhash.gxhash128_nogil(input_bytes, seed)}!")
253+
```
254+
"""
255+
256+
257+
async def gxhash128_async(input_bytes: bytes, seed: int) -> int:
258+
"""
259+
Summary
260+
-------
261+
hashes an arbitrary stream of bytes to an u128 asynchronously in a Rust thread
262+
263+
264+
Parameters
265+
----------
266+
input_bytes (bytes): input bytes to hash
267+
268+
seed (int): seed for the hash function
269+
270+
271+
Returns
272+
-------
273+
hash (int): u128 hash of the input bytes
274+
275+
276+
Example
277+
-------
278+
```python
279+
import gxhash
280+
import asyncio
281+
282+
async def main():
283+
input_bytes = bytes([42] * 1000)
284+
seed = 1234
285+
print(f"Hash is {await gxhash.gxhash128_async(input_bytes, seed)}!")
286+
287+
asyncio.run(main())
288+
```
289+
"""

py-gxhash/src/lib.rs

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,90 @@
1+
use pyo3::exceptions::PyRuntimeError;
12
use pyo3::prelude::*;
3+
use pyo3_async_runtimes::tokio::future_into_py;
4+
use tokio::task::spawn_blocking;
25

36
#[pyfunction]
4-
fn gxhash32(input: &[u8], seed: i64) -> PyResult<u32> {
5-
Ok(gxhash::gxhash32(input, seed))
7+
fn gxhash32(input_bytes: &[u8], seed: i64) -> PyResult<u32> {
8+
Ok(gxhash::gxhash32(input_bytes, seed))
69
}
710

811
#[pyfunction]
9-
fn gxhash64(input: &[u8], seed: i64) -> PyResult<u64> {
10-
Ok(gxhash::gxhash64(input, seed))
12+
fn gxhash32_nogil(py: Python, input_bytes: &[u8], seed: i64) -> PyResult<u32> {
13+
py.allow_threads(|| Ok(gxhash::gxhash32(input_bytes, seed)))
1114
}
1215

1316
#[pyfunction]
14-
fn gxhash128(input: &[u8], seed: i64) -> PyResult<u128> {
15-
Ok(gxhash::gxhash128(input, seed))
17+
fn gxhash32_async<'p>(py: Python<'p>, input_bytes: &'p [u8], seed: i64) -> PyResult<Bound<'p, PyAny>> {
18+
let input_bytes_clone = input_bytes.to_vec();
19+
20+
future_into_py(py, async move {
21+
let result = Python::with_gil(|py| py.allow_threads(|| spawn_blocking(move || gxhash::gxhash32(&input_bytes_clone, seed)))).await;
22+
23+
match result {
24+
Ok(result) => Ok(result),
25+
Err(e) => Err(PyRuntimeError::new_err(format!("Task failed: {:?}", e))),
26+
}
27+
})
28+
}
29+
30+
#[pyfunction]
31+
fn gxhash64(input_bytes: &[u8], seed: i64) -> PyResult<u64> {
32+
Ok(gxhash::gxhash64(input_bytes, seed))
33+
}
34+
35+
#[pyfunction]
36+
fn gxhash64_nogil(py: Python, input_bytes: &[u8], seed: i64) -> PyResult<u64> {
37+
py.allow_threads(|| Ok(gxhash::gxhash64(input_bytes, seed)))
38+
}
39+
40+
#[pyfunction]
41+
fn gxhash64_async<'p>(py: Python<'p>, input_bytes: &'p [u8], seed: i64) -> PyResult<Bound<'p, PyAny>> {
42+
let input_bytes_clone = input_bytes.to_vec();
43+
44+
future_into_py(py, async move {
45+
let result = Python::with_gil(|py| py.allow_threads(|| spawn_blocking(move || gxhash::gxhash64(&input_bytes_clone, seed)))).await;
46+
47+
match result {
48+
Ok(result) => Ok(result),
49+
Err(e) => Err(PyRuntimeError::new_err(format!("Task failed: {:?}", e))),
50+
}
51+
})
52+
}
53+
54+
#[pyfunction]
55+
fn gxhash128(input_bytes: &[u8], seed: i64) -> PyResult<u128> {
56+
Ok(gxhash::gxhash128(input_bytes, seed))
57+
}
58+
59+
#[pyfunction]
60+
fn gxhash128_nogil(py: Python, input_bytes: &[u8], seed: i64) -> PyResult<u128> {
61+
py.allow_threads(|| Ok(gxhash::gxhash128(input_bytes, seed)))
62+
}
63+
64+
#[pyfunction]
65+
fn gxhash128_async<'p>(py: Python<'p>, input_bytes: &'p [u8], seed: i64) -> PyResult<Bound<'p, PyAny>> {
66+
let input_bytes_clone = input_bytes.to_vec();
67+
68+
future_into_py(py, async move {
69+
let result = Python::with_gil(|py| py.allow_threads(|| spawn_blocking(move || gxhash::gxhash128(&input_bytes_clone, seed)))).await;
70+
71+
match result {
72+
Ok(result) => Ok(result),
73+
Err(e) => Err(PyRuntimeError::new_err(format!("Task failed: {:?}", e))),
74+
}
75+
})
1676
}
1777

1878
#[pymodule(name = "gxhash")]
1979
fn pygxhash(m: &Bound<'_, PyModule>) -> PyResult<()> {
2080
m.add_function(wrap_pyfunction!(gxhash32, m)?)?;
81+
m.add_function(wrap_pyfunction!(gxhash32_nogil, m)?)?;
82+
m.add_function(wrap_pyfunction!(gxhash32_async, m)?)?;
2183
m.add_function(wrap_pyfunction!(gxhash64, m)?)?;
84+
m.add_function(wrap_pyfunction!(gxhash64_nogil, m)?)?;
85+
m.add_function(wrap_pyfunction!(gxhash64_async, m)?)?;
2286
m.add_function(wrap_pyfunction!(gxhash128, m)?)?;
87+
m.add_function(wrap_pyfunction!(gxhash128_nogil, m)?)?;
88+
m.add_function(wrap_pyfunction!(gxhash128_async, m)?)?;
2389
Ok(())
2490
}

0 commit comments

Comments
 (0)