spookyhash 2.1.2__cp315-cp315-win32.whl

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.
spookyhash/__init__.py ADDED
@@ -0,0 +1,243 @@
1
+ """A Python wrapper for SpookyHash version 2."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import struct
6
+ import typing
7
+ from abc import ABC, abstractmethod
8
+ from binascii import hexlify
9
+
10
+ from . import _spookyhash
11
+
12
+ if typing.TYPE_CHECKING:
13
+ import typing_extensions
14
+
15
+ _MessageT = bytes | memoryview[bytes] | typing_extensions.Buffer
16
+
17
+ __all__ = ("hash32", "hash64", "hash128", "Hash32", "Hash64", "Hash128")
18
+ __version__ = "2.1.2"
19
+
20
+
21
+ # Utility function for homogenising data
22
+
23
+ def _char_arr(message: _MessageT | None) -> memoryview[bytes]:
24
+ if message is None:
25
+ return memoryview(b"").cast("c")
26
+ if not isinstance(message, memoryview):
27
+ try:
28
+ message = memoryview(message)
29
+ except TypeError as e:
30
+ raise TypeError(
31
+ f"Expecting message to be bytes, memoryview, None, or a type "
32
+ f"that can be interpreted as memoryview (e.g. NumPy "
33
+ f"arrays); instead found "
34
+ f"{type(message).__module__}.{type(message).__name__}"
35
+ ) from e
36
+ if message.format != "c" or not message.contiguous:
37
+ message = message.cast("c")
38
+ if typing.TYPE_CHECKING:
39
+ message = typing.cast(memoryview[bytes], message)
40
+ return message
41
+
42
+
43
+ # Oneshot hash functions
44
+
45
+
46
+ def hash32(message: _MessageT, seed: int = 0) -> int:
47
+ """
48
+ Hash a single message in one call, produce 32-bit output.
49
+
50
+ :param message: message to hash
51
+ :param seed: 32-bit seed
52
+ :return: 32-bit hash
53
+ """
54
+ return _spookyhash.hash32(_char_arr(message), seed)
55
+
56
+
57
+ def hash64(message: _MessageT, seed: int = 0) -> int:
58
+ """
59
+ Hash a single message in one call, produce 64-bit output.
60
+
61
+ :param message: message to hash
62
+ :param seed: 64-bit seed
63
+ :return: 64-bit hash
64
+ """
65
+ return _spookyhash.hash64(_char_arr(message), seed)
66
+
67
+
68
+ def hash128_pair(
69
+ message: _MessageT,
70
+ seed1: int = 0,
71
+ seed2: int = 0,
72
+ ) -> tuple[int, int]:
73
+ """
74
+ Hash a single message in one call, produce 128-bit output.
75
+
76
+ :param message: message to hash
77
+ :param seed1: 64-bit seed 1
78
+ :param seed2: 64-bit seed 2
79
+ :return: Pair of 64-bit hash values
80
+ """
81
+ digest = _spookyhash.hash128(_char_arr(message), seed1=seed1, seed2=seed2)
82
+ return struct.unpack("=QQ", digest)
83
+
84
+
85
+ def hash128(
86
+ message: _MessageT,
87
+ seed1: int = 0,
88
+ seed2: int = 0,
89
+ ) -> int:
90
+ """
91
+ Hash a single message in one call, produce 128-bit output.
92
+
93
+ :param message: message to hash
94
+ :param seed1: 64-bit seed 1
95
+ :param seed2: 64-bit seed 2
96
+ :return: 128-bit hash
97
+ """
98
+ val1, val2 = hash128_pair(message, seed1=seed1, seed2=seed2)
99
+ return val2 << 64 | val1
100
+
101
+
102
+ # Incremental hash builders
103
+
104
+
105
+ class Hash(ABC):
106
+ """Base class for incremental hash builders."""
107
+
108
+ digest_size: typing.ClassVar[int]
109
+ _hash_cls: typing.ClassVar[type[_spookyhash.Hash]]
110
+
111
+ def __init__(
112
+ self,
113
+ message: _MessageT | None = None,
114
+ seed1: int = 0,
115
+ seed2: int = 0,
116
+ ) -> None:
117
+ """
118
+ Initialise hash.
119
+
120
+ :param message: Optional initial message fragment
121
+ :param seed1: first seed
122
+ :param seed2: second seed
123
+ """
124
+ self._hash = self._hash_cls(seed1, seed2)
125
+ if message is not None:
126
+ self.update(message)
127
+
128
+ def __init_subclass__(cls) -> None:
129
+ super().__init_subclass__()
130
+ cls.digest_size = cls._hash_cls(0, 0).digest_size
131
+
132
+ def update(self, message: _MessageT | None) -> typing_extensions.Self:
133
+ """
134
+ Incrementally update hash with message fragment.
135
+
136
+ :param message: Message fragment
137
+ :return: Self
138
+ """
139
+ if message is not None:
140
+ self._hash.update(_char_arr(message))
141
+ return self
142
+
143
+ def digest(self) -> bytes:
144
+ """
145
+ Compute digest.
146
+
147
+ :return: Digest
148
+ """
149
+ return self._hash.digest()
150
+
151
+ def hexdigest(self) -> str:
152
+ """
153
+ Compute hex digest.
154
+
155
+ :return: Hex digest
156
+ """
157
+ return hexlify(self.digest()).decode("utf-8")
158
+
159
+ @abstractmethod
160
+ def final(self) -> int:
161
+ """
162
+ Compute the hash for the concatenation of all message fragments.
163
+
164
+ :return: Hash
165
+ """
166
+ raise NotImplementedError
167
+
168
+
169
+ class Hash32(Hash):
170
+ """32-bit incremental hash builder."""
171
+
172
+ _hash_cls = _spookyhash.Hash32
173
+
174
+ def __init__(
175
+ self,
176
+ message: _MessageT | None = None,
177
+ seed: int = 0,
178
+ ) -> None:
179
+ """
180
+ Initialise 32-bit hash.
181
+
182
+ :param message: Optional initial message fragment
183
+ :param seed: 32-bit seed
184
+ """
185
+ super().__init__(message, seed, seed)
186
+
187
+ def final(self) -> int:
188
+ """
189
+ Compute the hash for the concatenation of all message fragments.
190
+
191
+ :return: Hash
192
+ """
193
+ return typing.cast(int, struct.unpack("=L", self.digest())[0])
194
+
195
+
196
+ class Hash64(Hash):
197
+
198
+ _hash_cls = _spookyhash.Hash64
199
+
200
+ def __init__(
201
+ self,
202
+ message: _MessageT | None = None,
203
+ seed: int = 0,
204
+ ) -> None:
205
+ """
206
+ Initialise 64-bit hash.
207
+
208
+ :param message: Optional initial message fragment
209
+ :param seed: 64-bit seed
210
+ """
211
+ super().__init__(message, seed, seed)
212
+
213
+ def final(self) -> int:
214
+ """
215
+ Compute the hash for the concatenation of all message fragments.
216
+
217
+ :return: Hash
218
+ """
219
+ return typing.cast(int, struct.unpack("=Q", self.digest())[0])
220
+
221
+
222
+ class Hash128(Hash):
223
+
224
+ _hash_cls = _spookyhash.Hash128
225
+
226
+ def final_pair(self) -> tuple[int, int]:
227
+ """
228
+ Compute the hash for the concatenation of all message fragments.
229
+
230
+ Returns a pair of 64-bit integer to match other common implementations.
231
+
232
+ :return: Pair of 64-bit integers representing the hash
233
+ """
234
+ return struct.unpack("=QQ", self.digest())
235
+
236
+ def final(self) -> int:
237
+ """
238
+ Compute the hash for the concatenation of all message fragments.
239
+
240
+ :return: Hash
241
+ """
242
+ hash1, hash2 = self.final_pair()
243
+ return hash2 << 64 | hash1
Binary file
@@ -0,0 +1,25 @@
1
+ """Type stubs for SpookyHash Cython code."""
2
+
3
+ import typing
4
+
5
+ import typing_extensions
6
+
7
+ __all__ = ("hash32", "hash64", "hash128", "Hash", "Hash32", "Hash64", "Hash128")
8
+ __version__: str
9
+
10
+ def hash32(message: bytes | memoryview[bytes], seed: int = 0) -> int: ...
11
+ def hash64(message: bytes | memoryview[bytes], seed: int = 0) -> int: ...
12
+ def hash128(message: bytes | memoryview[bytes], seed1: int = 0, seed2: int = 0) -> bytes: ...
13
+
14
+ @typing_extensions.disjoint_base
15
+ class Hash:
16
+ def __new__(cls, seed1: int, seed2: int) -> typing_extensions.Self: ...
17
+
18
+ digest_size: int
19
+
20
+ def update(self, message: bytes | memoryview[bytes]) -> None: ...
21
+ def digest(self) -> bytes: ...
22
+
23
+ class Hash32(Hash): ...
24
+ class Hash64(Hash): ...
25
+ class Hash128(Hash): ...
spookyhash/py.typed ADDED
File without changes
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.4
2
+ Name: spookyhash
3
+ Version: 2.1.2
4
+ Summary: A Python wrapper for SpookyHash version 2
5
+ Author-email: Alen Buhanec <alen.buhanec@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/buhanec/spookyhash
8
+ Project-URL: Repository, https://github.com/buhanec/spookyhash
9
+ Project-URL: Issues, https://github.com/buhanec/spookyhash/issues
10
+ Keywords: SpookyHash,hashlib
11
+ Classifier: Topic :: Software Development :: Libraries
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Programming Language :: Python :: 3.15
21
+ Classifier: Operating System :: OS Independent
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Provides-Extra: typing
27
+ Requires-Dist: mypy>=1.18; extra == "typing"
28
+ Requires-Dist: typing-extensions; extra == "typing"
29
+ Provides-Extra: test
30
+ Requires-Dist: pytest; extra == "test"
31
+ Requires-Dist: hypothesis; extra == "test"
32
+ Requires-Dist: numpy; (python_version >= "3.11" or sys_platform != "win32" or platform_machine != "ARM64") and extra == "test"
33
+ Dynamic: license-file
34
+
35
+ # spookyhash
36
+
37
+ A Python wrapper of Bob Jenkins' [SpookyHash version 2](http://burtleburtle.net/bob/hash/spooky.html). Offers 32- 64- and 128-bit oneshot and incremental hashes.
38
+
39
+ # License
40
+
41
+ > Licensed under the MIT license. See the LICENSE file in the repository root for more details.
42
+
43
+ # Usage
44
+
45
+ ## Installation
46
+
47
+ Available through [spookyhash - PyPI](https://pypi.org/project/spookyhash/) using `pip install spookyhash`.
48
+
49
+ ## Oneshot Hashes
50
+
51
+ ```python
52
+ >>> import spookyhash
53
+
54
+ >>> spookyhash.hash32(b'hello world')
55
+ 2617184861
56
+ >>> spookyhash.hash32(b'hello world', seed=0x12345678)
57
+ 3380090220
58
+
59
+ >>> spookyhash.hash64(b'hello world')
60
+ 14865987102431973981
61
+ >>> spookyhash.hash64(b'hello world', seed=123)
62
+ 5719863273689036421
63
+
64
+ >>> spookyhash.hash128(b'hello world')
65
+ 185933735475381961281710998418114941533
66
+ >>> spookyhash.hash128(b'hello world', seed1=123_000, seed2=456_000)
67
+ 144121310386202441278894605216246194925
68
+
69
+ >>> # For a more comparable result to other libraries
70
+ >>> spookyhash.hash128_pair(b'hello world')
71
+ (14865987102431973981, 10079487997037711397)
72
+ >>> spookyhash.hash128_pair(b'hello world', seed1=123_000, seed2=456_000)
73
+ (12678109464562819821, 7812831891108919044)
74
+ ```
75
+
76
+ ## Incremental Hashes
77
+
78
+ ```python
79
+ >>> import spookyhash
80
+
81
+ >>> sh = spookyhash.Hash32()
82
+ >>> sh.update(b'hello')
83
+ >>> sh.update(b' ')
84
+ >>> sh.update(b'world')
85
+ >>> sh.final()
86
+ 2617184861
87
+ >>> sh.hexdigest()
88
+ '5d12ff9b'
89
+
90
+ >>> spookyhash.Hash64(b'hello ', seed=123).update(b'world').final()
91
+ 5719863273689036421
92
+
93
+ >>> spookyhash.Hash64(b'hello ', seed=123).update(b'world').hexdigest()
94
+ '85b609a05709614f'
95
+
96
+ >>> sh = spookyhash.Hash128(seed1=123_000, seed2=456_000)
97
+ >>> sh.update(b'hello world')
98
+ >>> sh.final()
99
+ 144121310386202441278894605216246194925
100
+ >>> sh.final_pair()
101
+ (12678109464562819821, 7812831891108919044)
102
+ >>> sh.hexdigest()
103
+ 'ede2c8f262b1f1af04f763f735c16c6c'
104
+ ```
105
+
106
+ ## `memoryview` Support
107
+
108
+ Includes `memoryview` compatible types, such as NumPy arrays.
109
+
110
+ ```python
111
+ >>> import spookyhash
112
+ >>> import numpy as np
113
+
114
+ >>> spookyhash.Hash64(np.arange(100, dtype=np.int32)).hexdigest()
115
+ '43ab5363ad362c74'
116
+
117
+ >>> spookyhash.Hash64(memoryview(b'hello world')).hexdigest()
118
+ '5d12ff9b81984ece'
119
+ ```
120
+
121
+ # Platform Independence
122
+
123
+ If run on a big-endian system, the code would produce different hashes, but of equal quality.
@@ -0,0 +1,9 @@
1
+ spookyhash/__init__.py,sha256=oZjA7qdTpUzEfkpThNTWZR4VKWIweHYaIJXtYiWfibY,6335
2
+ spookyhash/_spookyhash.cp315-win32.pyd,sha256=BH85roGVARwVMssODOy6Qb2ZioH3_T8NISCBju1KAho,144896
3
+ spookyhash/_spookyhash.pyi,sha256=6tnHILp49jP7vQd30axW9gTNta53D8qICur8nfQxwHU,777
4
+ spookyhash/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ spookyhash-2.1.2.dist-info/licenses/LICENSE,sha256=_ap0xdBj8tPRjmdLWBpcz90D4o1c8YMe1hlOS4mT_6o,1090
6
+ spookyhash-2.1.2.dist-info/METADATA,sha256=e13xm68lpsOiYALkS3ed8ImvpmdFAHDiHzkZU4pIVMM,3837
7
+ spookyhash-2.1.2.dist-info/WHEEL,sha256=hw69xxiYuOcOqid9WoScsymTbZ3eDs5HQ4gUZZtdDhs,97
8
+ spookyhash-2.1.2.dist-info/top_level.txt,sha256=zl2dLX8mCzkff0kkntLozTPeYaB2IZxT7_xQLb8s0NQ,11
9
+ spookyhash-2.1.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: false
4
+ Tag: cp315-cp315-win32
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Alen Buhanec
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ spookyhash