siphashc 2.7__tar.gz → 2.8__tar.gz

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.
@@ -1,6 +1,14 @@
1
1
  Changes
2
2
  =======
3
3
 
4
+ 2.8
5
+ ---
6
+
7
+ * Added support for Python 3.15, including free-threaded builds.
8
+ * Fixed SipHash input decoding on platforms with different alignment or byte
9
+ order.
10
+ * Improved hashing performance and enabled concurrent hashing of large inputs.
11
+
4
12
  2.7
5
13
  ---
6
14
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: siphashc
3
- Version: 2.7
3
+ Version: 2.8
4
4
  Summary: Python module (in c) for siphash-2-4
5
5
  Author-email: Michal Čihař <michal@weblate.org>
6
6
  License-Expression: ISC
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Programming Language :: Python :: 3.15
23
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
25
  Requires-Python: >=3.10
25
26
  Description-Content-Type: text/x-rst
@@ -11,10 +11,10 @@ lint = [
11
11
  {include-group = "pre-commit"}
12
12
  ]
13
13
  pre-commit = [
14
- "pre-commit==4.3.0"
14
+ "prek==0.4.11"
15
15
  ]
16
16
  test = [
17
- "pytest==8.4.2"
17
+ "pytest==9.1.1"
18
18
  ]
19
19
 
20
20
  [project]
@@ -29,6 +29,7 @@ classifiers = [
29
29
  "Programming Language :: Python :: 3.12",
30
30
  "Programming Language :: Python :: 3.13",
31
31
  "Programming Language :: Python :: 3.14",
32
+ "Programming Language :: Python :: 3.15",
32
33
  "Topic :: Software Development :: Libraries :: Python Modules"
33
34
  ]
34
35
  description = "Python module (in c) for siphash-2-4"
@@ -37,7 +38,7 @@ license = "ISC"
37
38
  license-files = ["LICENSE.md"]
38
39
  name = "siphashc"
39
40
  requires-python = ">=3.10"
40
- version = "2.7"
41
+ version = "2.8"
41
42
 
42
43
  [[project.authors]]
43
44
  email = "michal@weblate.org"
@@ -80,6 +81,7 @@ docstring-code-format = true
80
81
 
81
82
  [tool.ruff.lint]
82
83
  ignore = [
84
+ "CPY001", # TODO: update copyright info
83
85
  "S101", # CONFIG: using pytest
84
86
  "D203", # CONFIG: incompatible with D211
85
87
  "D212" # CONFIG: incompatible with D213
@@ -96,6 +98,20 @@ max-complexity = 16
96
98
  [tool.ruff.lint.per-file-ignores]
97
99
  "benchmark.py" = ["T201"]
98
100
 
101
+ [tool.rumdl]
102
+ disable = [
103
+ # We do not enforce heading
104
+ "first-line-heading",
105
+ # Avoid length limit checks
106
+ "line-length"
107
+ ]
108
+
109
+ [tool.rumdl.per-file-ignores]
110
+ "README.md" = [
111
+ # Allow HTML
112
+ "MD033"
113
+ ]
114
+
99
115
  [tool.setuptools]
100
116
  include-package-data = true
101
117
  zip-safe = true
@@ -5,13 +5,14 @@ import sysconfig
5
5
 
6
6
  from setuptools import Extension, setup
7
7
 
8
+ free_threaded = bool(sysconfig.get_config_var("Py_GIL_DISABLED"))
8
9
  define_macros = [("Py_LIMITED_API", "0x030A0000")]
9
10
  bdist_options = {"py_limited_api": "cp310"}
10
11
 
11
- # Free-threaded Python is not compatible with ABI3, see
12
- # https://github.com/python/cpython/issues/111506
13
- if sysconfig.get_config_var("Py_GIL_DISABLED"):
14
- define_macros = []
12
+ # Build version-specific free-threaded wheels until setuptools supports ABI3T.
13
+ # https://github.com/pypa/setuptools/issues/5205
14
+ if free_threaded:
15
+ define_macros = [("Py_GIL_DISABLED", "1")]
15
16
  bdist_options = {}
16
17
 
17
18
  setup(
@@ -21,7 +22,7 @@ setup(
21
22
  sources=["siphashc.c", "siphash/siphash.c"],
22
23
  language="c",
23
24
  define_macros=define_macros,
24
- py_limited_api=True,
25
+ py_limited_api=not free_threaded,
25
26
  ),
26
27
  ],
27
28
  options={"bdist_wheel": bdist_options},
@@ -0,0 +1 @@
1
+ siphash from <https://github.com/floodyberry/siphash>
@@ -4,7 +4,14 @@
4
4
 
5
5
  INLINE static uint64_t
6
6
  U8TO64_LE(const unsigned char *p) {
7
- return *(const uint64_t *)p;
7
+ return ((uint64_t)p[0])
8
+ | ((uint64_t)p[1] << 8)
9
+ | ((uint64_t)p[2] << 16)
10
+ | ((uint64_t)p[3] << 24)
11
+ | ((uint64_t)p[4] << 32)
12
+ | ((uint64_t)p[5] << 40)
13
+ | ((uint64_t)p[6] << 48)
14
+ | ((uint64_t)p[7] << 56);
8
15
  }
9
16
 
10
17
  uint64_t
@@ -0,0 +1,191 @@
1
+ /*
2
+ * Copyright (c) 2013 Eli Janssen
3
+ * Copyright (c) 2014 Carlo Pires
4
+ * Copyright © 2017–2023 Michal Čihař
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ **/
24
+
25
+ #define PY_SSIZE_T_CLEAN
26
+
27
+ #include <Python.h>
28
+ #include <stdlib.h>
29
+ #include <string.h>
30
+ #include "siphash/siphash.h"
31
+
32
+ #define SIPHASH_GIL_THRESHOLD 8192
33
+ #if PY_VERSION_HEX < 0x030B0000
34
+ #define SIPHASHC_PYC_FUNCTION_CAST(func) \
35
+ ((PyCFunction)(void (*)(void))(func))
36
+ #else
37
+ #define SIPHASHC_PYC_FUNCTION_CAST(func) _PyCFunction_CAST(func)
38
+ #endif
39
+
40
+ static int get_immutable_data(
41
+ PyObject *object,
42
+ const char *argument_name,
43
+ const char **data,
44
+ Py_ssize_t *size) {
45
+ if (PyBytes_Check(object)) {
46
+ char *bytes_data;
47
+
48
+ if (PyBytes_AsStringAndSize(object, &bytes_data, size) < 0) {
49
+ return 0;
50
+ }
51
+ *data = bytes_data;
52
+ return 1;
53
+ }
54
+
55
+ if (PyUnicode_Check(object)) {
56
+ *data = PyUnicode_AsUTF8AndSize(object, size);
57
+ return *data != NULL;
58
+ }
59
+
60
+ PyErr_Format(
61
+ PyExc_TypeError,
62
+ "%s must be str or bytes",
63
+ argument_name);
64
+ return 0;
65
+ }
66
+
67
+ static PyObject *pysiphash(
68
+ PyObject *self,
69
+ PyObject *const *args,
70
+ Py_ssize_t nargs) {
71
+ PyObject *key_object;
72
+ PyObject *plaintext_object;
73
+ const char *key = NULL;
74
+ Py_ssize_t key_sz;
75
+ const char *plaintext = NULL;
76
+ Py_ssize_t plain_sz;
77
+ uint64_t hash;
78
+
79
+ if (nargs != 2) {
80
+ PyErr_Format(
81
+ PyExc_TypeError,
82
+ "siphash() takes exactly 2 arguments (%zd given)",
83
+ nargs);
84
+ return NULL;
85
+ }
86
+ key_object = args[0];
87
+ plaintext_object = args[1];
88
+
89
+ /* Both objects own immutable storage and remain alive for the call. */
90
+ if (!get_immutable_data(key_object, "key", &key, &key_sz) ||
91
+ !get_immutable_data(
92
+ plaintext_object,
93
+ "plaintext",
94
+ &plaintext,
95
+ &plain_sz)) {
96
+ return NULL;
97
+ }
98
+
99
+ if (key_sz != 16) {
100
+ PyErr_SetString(
101
+ PyExc_ValueError,
102
+ "key must be exactly 128 bits long (16 chars)");
103
+ return NULL;
104
+ }
105
+
106
+ #ifdef Py_GIL_DISABLED
107
+ hash = siphash(
108
+ (const unsigned char *)key,
109
+ (const unsigned char *)plaintext,
110
+ plain_sz);
111
+ #else
112
+ if (plain_sz >= SIPHASH_GIL_THRESHOLD) {
113
+ Py_BEGIN_ALLOW_THREADS
114
+ hash = siphash(
115
+ (const unsigned char *)key,
116
+ (const unsigned char *)plaintext,
117
+ plain_sz);
118
+ Py_END_ALLOW_THREADS
119
+ } else {
120
+ hash = siphash(
121
+ (const unsigned char *)key,
122
+ (const unsigned char *)plaintext,
123
+ plain_sz);
124
+ }
125
+ #endif
126
+
127
+ return PyLong_FromUnsignedLongLong(hash);
128
+ }
129
+
130
+ static char siphash_docstring[] = ""
131
+ "Computes Siphash-2-4 of the given string and key\n\n"
132
+ "siphash(key, plaintext) -> hash\n"
133
+ " - key: must be 128 bit long (16 chars at 8 bit each)\n"
134
+ " - plaintext: text\n"
135
+ "returns 64-bit output (python Long)\n";
136
+
137
+ static PyMethodDef siphashc_methods[] = {
138
+ {"siphash", SIPHASHC_PYC_FUNCTION_CAST(pysiphash), METH_FASTCALL,
139
+ siphash_docstring},
140
+ {NULL, NULL, 0, NULL} /* sentinel */
141
+ };
142
+
143
+ #ifdef Py_TARGET_ABI3T
144
+
145
+ PyABIInfo_VAR(abi_info);
146
+
147
+ static PySlot siphashc_slots[] = {
148
+ PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
149
+ PySlot_STATIC_DATA(Py_mod_name, "siphashc"),
150
+ PySlot_STATIC_DATA(Py_mod_methods, siphashc_methods),
151
+ PySlot_STATIC_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
152
+ PySlot_END,
153
+ };
154
+
155
+ PyMODEXPORT_FUNC
156
+ PyModExport_siphashc(void)
157
+ {
158
+ return siphashc_slots;
159
+ }
160
+
161
+ #else
162
+
163
+ static struct PyModuleDef moduledef = {
164
+ PyModuleDef_HEAD_INIT,
165
+ "siphashc",
166
+ NULL,
167
+ -1,
168
+ siphashc_methods,
169
+ NULL,
170
+ NULL,
171
+ NULL,
172
+ NULL
173
+ };
174
+
175
+ PyObject *
176
+ PyInit_siphashc(void)
177
+ {
178
+ PyObject *module = PyModule_Create(&moduledef);
179
+
180
+ #ifdef Py_GIL_DISABLED
181
+ if (module != NULL &&
182
+ PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED) < 0) {
183
+ Py_DECREF(module);
184
+ return NULL;
185
+ }
186
+ #endif
187
+
188
+ return module;
189
+ }
190
+
191
+ #endif
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: siphashc
3
- Version: 2.7
3
+ Version: 2.8
4
4
  Summary: Python module (in c) for siphash-2-4
5
5
  Author-email: Michal Čihař <michal@weblate.org>
6
6
  License-Expression: ISC
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Programming Language :: Python :: 3.15
23
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
25
  Requires-Python: >=3.10
25
26
  Description-Content-Type: text/x-rst
@@ -0,0 +1,236 @@
1
+ """Test for siphashc module."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ import sysconfig
7
+ import threading
8
+ import time
9
+ import unittest
10
+
11
+ import pytest
12
+
13
+ from siphashc import siphash
14
+
15
+ GIL_THRESHOLD = 8192
16
+ GIL_TEST_ITERATIONS = 10_000
17
+
18
+
19
+ class BytesSubclass(bytes):
20
+ """Bytes subclass used to verify accepted immutable inputs."""
21
+
22
+
23
+ class StringSubclass(str):
24
+ """String subclass used to verify accepted immutable inputs."""
25
+
26
+ __slots__ = ()
27
+
28
+
29
+ class TestSiphashC(unittest.TestCase):
30
+ """Test for siphashc module."""
31
+
32
+ @pytest.mark.skipif(
33
+ not sysconfig.get_config_var("Py_GIL_DISABLED"),
34
+ reason="requires free-threaded Python",
35
+ )
36
+ def test_gil_disabled(self: TestSiphashC) -> None:
37
+ """Test that importing the module does not enable the GIL."""
38
+ assert not sys._is_gil_enabled() # noqa: SLF001
39
+
40
+ def test_hash(self: TestSiphashC) -> None:
41
+ """Test simple hashing."""
42
+ result = siphash("sixteencharstrng", "i need a hash of this")
43
+ assert result == 10796923698683394048 # noqa: PLR2004
44
+
45
+ result = siphash("0123456789ABCDEF", "a")
46
+ assert result == 12398370950267227270 # noqa: PLR2004
47
+
48
+ def test_immutable_inputs(self: TestSiphashC) -> None:
49
+ """Test accepted immutable input types."""
50
+ expected = siphash(b"0123456789ABCDEF", b"a")
51
+
52
+ assert siphash("0123456789ABCDEF", "a") == expected
53
+ assert (
54
+ siphash(
55
+ BytesSubclass(b"0123456789ABCDEF"),
56
+ BytesSubclass(b"a"),
57
+ )
58
+ == expected
59
+ )
60
+ assert (
61
+ siphash(
62
+ StringSubclass("0123456789ABCDEF"),
63
+ StringSubclass("a"),
64
+ )
65
+ == expected
66
+ )
67
+
68
+ def test_rejects_other_inputs(self: TestSiphashC) -> None:
69
+ """Test rejection of mutable and generic buffer inputs."""
70
+ invalid_keys = (
71
+ bytearray(b"0123456789ABCDEF"),
72
+ memoryview(b"0123456789ABCDEF"),
73
+ object(),
74
+ )
75
+ invalid_plaintexts = (bytearray(b"a"), memoryview(b"a"), object())
76
+
77
+ for key in invalid_keys:
78
+ with pytest.raises(TypeError, match="key must be str or bytes"):
79
+ siphash(key, b"a")
80
+
81
+ for plaintext in invalid_plaintexts:
82
+ with pytest.raises(TypeError, match="plaintext must be str or bytes"):
83
+ siphash(b"0123456789ABCDEF", plaintext)
84
+
85
+ def test_argument_count(self: TestSiphashC) -> None:
86
+ """Test positional argument count errors."""
87
+ for args in ((), (b"key",), (b"key", b"plaintext", b"extra")):
88
+ with pytest.raises(TypeError) as error:
89
+ siphash(*args)
90
+ assert str(error.value) == (
91
+ f"siphash() takes exactly 2 arguments ({len(args)} given)"
92
+ )
93
+
94
+ def test_rejects_keywords(self: TestSiphashC) -> None:
95
+ """Test rejection of keyword arguments."""
96
+ with pytest.raises(
97
+ TypeError,
98
+ match=r"siphash\(\) takes no keyword arguments",
99
+ ):
100
+ siphash(key=b"0123456789ABCDEF", plaintext=b"a")
101
+
102
+ @pytest.mark.skipif(
103
+ bool(sysconfig.get_config_var("Py_GIL_DISABLED")),
104
+ reason="requires a GIL-enabled Python",
105
+ )
106
+ def test_gil_release_threshold(self: TestSiphashC) -> None:
107
+ """Test that only inputs at or above the threshold release the GIL."""
108
+ counter = 0
109
+ ready = threading.Event()
110
+ stop = threading.Event()
111
+
112
+ def count_in_thread() -> None:
113
+ nonlocal counter
114
+ ready.set()
115
+ while not stop.is_set():
116
+ counter += 1
117
+ time.sleep(0)
118
+
119
+ thread = threading.Thread(target=count_in_thread)
120
+ previous_interval = sys.getswitchinterval()
121
+ sys.setswitchinterval(1.0)
122
+ try:
123
+ thread.start()
124
+ assert ready.wait(timeout=5)
125
+
126
+ key = b"0123456789ABCDEF"
127
+ below_threshold = b"a" * (GIL_THRESHOLD - 1)
128
+ at_threshold = b"a" * GIL_THRESHOLD
129
+
130
+ before = counter
131
+ for _ in range(GIL_TEST_ITERATIONS):
132
+ siphash(key, below_threshold)
133
+ assert counter == before
134
+
135
+ before = counter
136
+ for _ in range(GIL_TEST_ITERATIONS):
137
+ siphash(key, at_threshold)
138
+ assert counter > before
139
+ finally:
140
+ stop.set()
141
+ sys.setswitchinterval(previous_interval)
142
+ thread.join(timeout=5)
143
+
144
+ assert not thread.is_alive()
145
+
146
+ def test_errors(self: TestSiphashC) -> None:
147
+ """Test error handling."""
148
+ with pytest.raises(
149
+ ValueError,
150
+ match=r"key must be exactly 128 bits long \(16 chars\)",
151
+ ):
152
+ siphash("not long enough", "a")
153
+ with pytest.raises(
154
+ ValueError,
155
+ match=r"key must be exactly 128 bits long \(16 chars\)",
156
+ ):
157
+ siphash("toooooooooooooooooooooooo long", "a")
158
+ with pytest.raises(
159
+ ValueError,
160
+ match=r"key must be exactly 128 bits long \(16 chars\)",
161
+ ):
162
+ siphash("", "a")
163
+
164
+ def test_reference_vectors(self: TestSiphashC) -> None:
165
+ """Test reference vectors."""
166
+ vectors = [
167
+ 0x726FDB47DD0E0E31,
168
+ 0x74F839C593DC67FD,
169
+ 0x0D6C8009D9A94F5A,
170
+ 0x85676696D7FB7E2D,
171
+ 0xCF2794E0277187B7,
172
+ 0x18765564CD99A68D,
173
+ 0xCBC9466E58FEE3CE,
174
+ 0xAB0200F58B01D137,
175
+ 0x93F5F5799A932462,
176
+ 0x9E0082DF0BA9E4B0,
177
+ 0x7A5DBBC594DDB9F3,
178
+ 0xF4B32F46226BADA7,
179
+ 0x751E8FBC860EE5FB,
180
+ 0x14EA5627C0843D90,
181
+ 0xF723CA908E7AF2EE,
182
+ 0xA129CA6149BE45E5,
183
+ 0x3F2ACC7F57C29BDB,
184
+ 0x699AE9F52CBE4794,
185
+ 0x4BC1B3F0968DD39C,
186
+ 0xBB6DC91DA77961BD,
187
+ 0xBED65CF21AA2EE98,
188
+ 0xD0F2CBB02E3B67C7,
189
+ 0x93536795E3A33E88,
190
+ 0xA80C038CCD5CCEC8,
191
+ 0xB8AD50C6F649AF94,
192
+ 0xBCE192DE8A85B8EA,
193
+ 0x17D835B85BBB15F3,
194
+ 0x2F2E6163076BCFAD,
195
+ 0xDE4DAAACA71DC9A5,
196
+ 0xA6A2506687956571,
197
+ 0xAD87A3535C49EF28,
198
+ 0x32D892FAD841C342,
199
+ 0x7127512F72F27CCE,
200
+ 0xA7F32346F95978E3,
201
+ 0x12E0B01ABB051238,
202
+ 0x15E034D40FA197AE,
203
+ 0x314DFFBE0815A3B4,
204
+ 0x027990F029623981,
205
+ 0xCADCD4E59EF40C4D,
206
+ 0x9ABFD8766A33735C,
207
+ 0x0E3EA96B5304A7D0,
208
+ 0xAD0C42D6FC585992,
209
+ 0x187306C89BC215A9,
210
+ 0xD4A60ABCF3792B95,
211
+ 0xF935451DE4F21DF2,
212
+ 0xA9538F0419755787,
213
+ 0xDB9ACDDFF56CA510,
214
+ 0xD06C98CD5C0975EB,
215
+ 0xE612A3CB9ECBA951,
216
+ 0xC766E62CFCADAF96,
217
+ 0xEE64435A9752FE72,
218
+ 0xA192D576B245165A,
219
+ 0x0A8787BF8ECB74B2,
220
+ 0x81B3E73D20B49B6F,
221
+ 0x7FA8220BA3B2ECEA,
222
+ 0x245731C13CA42499,
223
+ 0xB78DBFAF3A8D83BD,
224
+ 0xEA1AD565322A1A0B,
225
+ 0x60E61C23A3795013,
226
+ 0x6606D7E446282B93,
227
+ 0x6CA4ECB15C5F91E1,
228
+ 0x9F626DA15C9625F3,
229
+ 0xE51B38608EF25F57,
230
+ 0x958A324CEB064572,
231
+ ]
232
+ k = bytes(range(16))
233
+ message = b""
234
+ for i in range(64):
235
+ assert siphash(k, message) == vectors[i]
236
+ message += bytes((i,))
@@ -1 +0,0 @@
1
- siphash from https://github.com/floodyberry/siphash
siphashc-2.7/siphashc.c DELETED
@@ -1,88 +0,0 @@
1
- /*
2
- * Copyright (c) 2013 Eli Janssen
3
- * Copyright (c) 2014 Carlo Pires
4
- * Copyright © 2017–2023 Michal Čihař
5
- *
6
- * Permission is hereby granted, free of charge, to any person obtaining a copy
7
- * of this software and associated documentation files (the "Software"), to deal
8
- * in the Software without restriction, including without limitation the rights
9
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- * copies of the Software, and to permit persons to whom the Software is
11
- * furnished to do so, subject to the following conditions:
12
- *
13
- * The above copyright notice and this permission notice shall be included in
14
- * all copies or substantial portions of the Software.
15
- *
16
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
- * THE SOFTWARE.
23
- **/
24
-
25
- #define PY_SSIZE_T_CLEAN
26
-
27
- #include <stdlib.h>
28
- #include <string.h>
29
- #include <Python.h>
30
- #include "siphash/siphash.h"
31
-
32
- static PyObject *pysiphash(PyObject *self, PyObject *args) {
33
- const char *key = NULL;
34
- Py_ssize_t key_sz;
35
- const char *plaintext = NULL;
36
- Py_ssize_t plain_sz;
37
- uint64_t hash;
38
-
39
- if (!PyArg_ParseTuple(
40
- args, "s#s#:siphash",
41
- &key, &key_sz, &plaintext, &plain_sz)) {
42
- return NULL;
43
- }
44
-
45
- if (key_sz != 16) {
46
- PyErr_SetString(
47
- PyExc_ValueError,
48
- "key must be exactly 128 bits long (16 chars)");
49
- return NULL;
50
- }
51
-
52
- hash = siphash(
53
- (const unsigned char*)key,
54
- (const unsigned char*)plaintext,
55
- plain_sz);
56
-
57
- return PyLong_FromUnsignedLongLong(hash);
58
- }
59
-
60
- static char siphash_docstring[] = ""
61
- "Computes Siphash-2-4 of the given string and key\n\n"
62
- "siphash(key, plaintext) -> hash\n"
63
- " - key: must be 128 bit long (16 chars at 8 bit each)\n"
64
- " - plaintext: text\n"
65
- "returns 64-bit output (python Long)\n";
66
-
67
- static PyMethodDef siphashc_methods[] = {
68
- {"siphash", pysiphash, METH_VARARGS, siphash_docstring},
69
- {NULL, NULL, 0, NULL} /* sentinel */
70
- };
71
-
72
- static struct PyModuleDef moduledef = {
73
- PyModuleDef_HEAD_INIT,
74
- "siphashc",
75
- NULL,
76
- -1,
77
- siphashc_methods,
78
- NULL,
79
- NULL,
80
- NULL,
81
- NULL
82
- };
83
-
84
- PyObject *
85
- PyInit_siphashc(void)
86
- {
87
- return PyModule_Create(&moduledef);
88
- }
@@ -1,113 +0,0 @@
1
- """Test for siphashc module."""
2
-
3
- from __future__ import annotations
4
-
5
- import unittest
6
-
7
- import pytest
8
-
9
- from siphashc import siphash
10
-
11
-
12
- class TestSiphashC(unittest.TestCase):
13
- """Test for siphashc module."""
14
-
15
- def test_hash(self: TestSiphashC) -> None:
16
- """Test simple hashing."""
17
- result = siphash("sixteencharstrng", "i need a hash of this")
18
- assert result == 10796923698683394048 # noqa: PLR2004
19
-
20
- result = siphash("0123456789ABCDEF", "a")
21
- assert result == 12398370950267227270 # noqa: PLR2004
22
-
23
- def test_errors(self: TestSiphashC) -> None:
24
- """Test error handling."""
25
- with pytest.raises(
26
- ValueError,
27
- match=r"key must be exactly 128 bits long \(16 chars\)",
28
- ):
29
- siphash("not long enough", "a")
30
- with pytest.raises(
31
- ValueError,
32
- match=r"key must be exactly 128 bits long \(16 chars\)",
33
- ):
34
- siphash("toooooooooooooooooooooooo long", "a")
35
- with pytest.raises(
36
- ValueError,
37
- match=r"key must be exactly 128 bits long \(16 chars\)",
38
- ):
39
- siphash("", "a")
40
-
41
- def test_reference_vectors(self: TestSiphashC) -> None:
42
- """Test reference vectors."""
43
- vectors = [
44
- 0x726FDB47DD0E0E31,
45
- 0x74F839C593DC67FD,
46
- 0x0D6C8009D9A94F5A,
47
- 0x85676696D7FB7E2D,
48
- 0xCF2794E0277187B7,
49
- 0x18765564CD99A68D,
50
- 0xCBC9466E58FEE3CE,
51
- 0xAB0200F58B01D137,
52
- 0x93F5F5799A932462,
53
- 0x9E0082DF0BA9E4B0,
54
- 0x7A5DBBC594DDB9F3,
55
- 0xF4B32F46226BADA7,
56
- 0x751E8FBC860EE5FB,
57
- 0x14EA5627C0843D90,
58
- 0xF723CA908E7AF2EE,
59
- 0xA129CA6149BE45E5,
60
- 0x3F2ACC7F57C29BDB,
61
- 0x699AE9F52CBE4794,
62
- 0x4BC1B3F0968DD39C,
63
- 0xBB6DC91DA77961BD,
64
- 0xBED65CF21AA2EE98,
65
- 0xD0F2CBB02E3B67C7,
66
- 0x93536795E3A33E88,
67
- 0xA80C038CCD5CCEC8,
68
- 0xB8AD50C6F649AF94,
69
- 0xBCE192DE8A85B8EA,
70
- 0x17D835B85BBB15F3,
71
- 0x2F2E6163076BCFAD,
72
- 0xDE4DAAACA71DC9A5,
73
- 0xA6A2506687956571,
74
- 0xAD87A3535C49EF28,
75
- 0x32D892FAD841C342,
76
- 0x7127512F72F27CCE,
77
- 0xA7F32346F95978E3,
78
- 0x12E0B01ABB051238,
79
- 0x15E034D40FA197AE,
80
- 0x314DFFBE0815A3B4,
81
- 0x027990F029623981,
82
- 0xCADCD4E59EF40C4D,
83
- 0x9ABFD8766A33735C,
84
- 0x0E3EA96B5304A7D0,
85
- 0xAD0C42D6FC585992,
86
- 0x187306C89BC215A9,
87
- 0xD4A60ABCF3792B95,
88
- 0xF935451DE4F21DF2,
89
- 0xA9538F0419755787,
90
- 0xDB9ACDDFF56CA510,
91
- 0xD06C98CD5C0975EB,
92
- 0xE612A3CB9ECBA951,
93
- 0xC766E62CFCADAF96,
94
- 0xEE64435A9752FE72,
95
- 0xA192D576B245165A,
96
- 0x0A8787BF8ECB74B2,
97
- 0x81B3E73D20B49B6F,
98
- 0x7FA8220BA3B2ECEA,
99
- 0x245731C13CA42499,
100
- 0xB78DBFAF3A8D83BD,
101
- 0xEA1AD565322A1A0B,
102
- 0x60E61C23A3795013,
103
- 0x6606D7E446282B93,
104
- 0x6CA4ECB15C5F91E1,
105
- 0x9F626DA15C9625F3,
106
- 0xE51B38608EF25F57,
107
- 0x958A324CEB064572,
108
- ]
109
- k = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
110
- message = ""
111
- for i in range(64):
112
- assert siphash(k, message) == vectors[i]
113
- message += chr(i)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes