| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent f885263 commit 828451d
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -49,3 +49,13 @@ See also the :c:member:`PyTypeObject.tp_hash` member. | |||
| 49 | 49 | :pep:`456` "Secure and interchangeable hash algorithm". | |
| 50 | 50 | ||
| 51 | 51 | .. versionadded:: 3.4 | |
| 52 | + | ||
| 53 | + | ||
| 54 | + .. c:function:: Py_hash_t Py_HashPointer(const void *ptr) | ||
| 55 | + | ||
| 56 | + Hash a pointer value: process the pointer value as an integer (cast it to | ||
| 57 | + ``uintptr_t`` internally). The pointer is not dereferenced. | ||
| 58 | + | ||
| 59 | + The function cannot fail: it cannot return ``-1``. | ||
| 60 | + | ||
| 61 | + .. versionadded:: 3.13 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1249,6 +1249,9 @@ New Features | |||
| 1249 | 1249 | :exc:`KeyError` if the key missing. | |
| 1250 | 1250 | (Contributed by Stefan Behnel and Victor Stinner in :gh:`111262`.) | |
| 1251 | 1251 | ||
| 1252 | + * Add :c:func:`Py_HashPointer` function to hash a pointer. | ||
| 1253 | + (Contributed by Victor Stinner in :gh:`111545`.) | ||
| 1254 | + | ||
| 1252 | 1255 | ||
| 1253 | 1256 | Porting to Python 3.13 | |
| 1254 | 1257 | ---------------------- | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -21,7 +21,9 @@ | |||
| 21 | 21 | ||
| 22 | 22 | /* Helpers for hash functions */ | |
| 23 | 23 | PyAPI_FUNC(Py_hash_t) _Py_HashDouble(PyObject *, double); | |
| 24 | - PyAPI_FUNC(Py_hash_t) _Py_HashPointer(const void*); | ||
| 24 | + | ||
| 25 | + // Kept for backward compatibility | ||
| 26 | + #define _Py_HashPointer Py_HashPointer | ||
| 25 | 27 | ||
| 26 | 28 | ||
| 27 | 29 | /* hash function definition */ | |
@@ -33,3 +35,5 @@ typedef struct { | |||
| 33 | 35 | } PyHash_FuncDef; | |
| 34 | 36 | ||
| 35 | 37 | PyAPI_FUNC(PyHash_FuncDef*) PyHash_GetFuncDef(void); | |
| 38 | + | ||
| 39 | + PyAPI_FUNC(Py_hash_t) Py_HashPointer(const void *ptr); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,8 +5,20 @@ | |||
| 5 | 5 | # error "this header requires Py_BUILD_CORE define" | |
| 6 | 6 | #endif | |
| 7 | 7 | ||
| 8 | - // Similar to _Py_HashPointer(), but don't replace -1 with -2 | ||
| 9 | - extern Py_hash_t _Py_HashPointerRaw(const void*); | ||
| 8 | + // Similar to Py_HashPointer(), but don't replace -1 with -2. | ||
| 9 | + static inline Py_hash_t | ||
| 10 | + _Py_HashPointerRaw(const void *ptr) | ||
| 11 | + { | ||
| 12 | + uintptr_t x = (uintptr_t)ptr; | ||
| 13 | + Py_BUILD_ASSERT(sizeof(x) == sizeof(ptr)); | ||
| 14 | + | ||
| 15 | + // Bottom 3 or 4 bits are likely to be 0; rotate x by 4 to the right | ||
| 16 | + // to avoid excessive hash collisions for dicts and sets. | ||
| 17 | + x = (x >> 4) | (x << (8 * sizeof(uintptr_t) - 4)); | ||
| 18 | + | ||
| 19 | + Py_BUILD_ASSERT(sizeof(x) == sizeof(Py_hash_t)); | ||
| 20 | + return (Py_hash_t)x; | ||
| 21 | + } | ||
| 10 | 22 | ||
| 11 | 23 | // Export for '_datetime' shared extension | |
| 12 | 24 | PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void*, Py_ssize_t); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,7 +4,8 @@ | |||
| 4 | 4 | _testcapi = import_helper.import_module('_testcapi') | |
| 5 | 5 | ||
| 6 | 6 | ||
| 7 | - SIZEOF_PY_HASH_T = _testcapi.SIZEOF_VOID_P | ||
| 7 | + SIZEOF_VOID_P = _testcapi.SIZEOF_VOID_P | ||
| 8 | + SIZEOF_PY_HASH_T = SIZEOF_VOID_P | ||
| 8 | 9 | ||
| 9 | 10 | ||
| 10 | 11 | class CAPITest(unittest.TestCase): | |
@@ -31,3 +32,48 @@ def test_hash_getfuncdef(self): | |||
| 31 | 32 | self.assertEqual(func_def.name, hash_info.algorithm) | |
| 32 | 33 | self.assertEqual(func_def.hash_bits, hash_info.hash_bits) | |
| 33 | 34 | self.assertEqual(func_def.seed_bits, hash_info.seed_bits) | |
| 35 | + | ||
| 36 | + def test_hash_pointer(self): | ||
| 37 | + # Test Py_HashPointer() | ||
| 38 | + hash_pointer = _testcapi.hash_pointer | ||
| 39 | + | ||
| 40 | + UHASH_T_MASK = ((2 ** (8 * SIZEOF_PY_HASH_T)) - 1) | ||
| 41 | + HASH_T_MAX = (2 ** (8 * SIZEOF_PY_HASH_T - 1) - 1) | ||
| 42 | + | ||
| 43 | + def python_hash_pointer(x): | ||
| 44 | + # Py_HashPointer() rotates the pointer bits by 4 bits to the right | ||
| 45 | + x = (x >> 4) | ((x & 15) << (8 * SIZEOF_VOID_P - 4)) | ||
| 46 | + | ||
| 47 | + # Convert unsigned uintptr_t (Py_uhash_t) to signed Py_hash_t | ||
| 48 | + if HASH_T_MAX < x: | ||
| 49 | + x = (~x) + 1 | ||
| 50 | + x &= UHASH_T_MASK | ||
| 51 | + x = (~x) + 1 | ||
| 52 | + return x | ||
| 53 | + | ||
| 54 | + if SIZEOF_VOID_P == 8: | ||
| 55 | + values = ( | ||
| 56 | + 0xABCDEF1234567890, | ||
| 57 | + 0x1234567890ABCDEF, | ||
| 58 | + 0xFEE4ABEDD1CECA5E, | ||
| 59 | + ) | ||
| 60 | + else: | ||
| 61 | + values = ( | ||
| 62 | + 0x12345678, | ||
| 63 | + 0x1234ABCD, | ||
| 64 | + 0xDEADCAFE, | ||
| 65 | + ) | ||
| 66 | + | ||
| 67 | + for value in values: | ||
| 68 | + expected = python_hash_pointer(value) | ||
| 69 | + with self.subTest(value=value): | ||
| 70 | + self.assertEqual(hash_pointer(value), expected, | ||
| 71 | + f"hash_pointer({value:x}) = " | ||
| 72 | + f"{hash_pointer(value):x} != {expected:x}") | ||
| 73 | + | ||
| 74 | + # Py_HashPointer(NULL) returns 0 | ||
| 75 | + self.assertEqual(hash_pointer(0), 0) | ||
| 76 | + | ||
| 77 | + # Py_HashPointer((void*)(uintptr_t)-1) doesn't return -1 but -2 | ||
| 78 | + VOID_P_MAX = -1 & (2 ** (8 * SIZEOF_VOID_P) - 1) | ||
| 79 | + self.assertEqual(hash_pointer(VOID_P_MAX), -2) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + Add :c:func:`Py_HashPointer` function to hash a pointer. Patch by Victor | ||
| 2 | + Stinner. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -44,8 +44,24 @@ hash_getfuncdef(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) | |||
| 44 | 44 | return result; | |
| 45 | 45 | } | |
| 46 | 46 | ||
| 47 | + | ||
| 48 | + static PyObject * | ||
| 49 | + hash_pointer(PyObject *Py_UNUSED(module), PyObject *arg) | ||
| 50 | + { | ||
| 51 | + void *ptr = PyLong_AsVoidPtr(arg); | ||
| 52 | + if (ptr == NULL && PyErr_Occurred()) { | ||
| 53 | + return NULL; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + Py_hash_t hash = Py_HashPointer(ptr); | ||
| 57 | + Py_BUILD_ASSERT(sizeof(long long) >= sizeof(hash)); | ||
| 58 | + return PyLong_FromLongLong(hash); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + | ||
| 47 | 62 | static PyMethodDef test_methods[] = { | |
| 48 | 63 | {"hash_getfuncdef", hash_getfuncdef, METH_NOARGS}, | |
| 64 | + {"hash_pointer", hash_pointer, METH_O}, | ||
| 49 | 65 | {NULL}, | |
| 50 | 66 | }; | |
| 51 | 67 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -45,7 +45,7 @@ | |||
| 45 | 45 | */ | |
| 46 | 46 | ||
| 47 | 47 | #include "Python.h" | |
| 48 | - #include "pycore_hashtable.h" | ||
| 48 | + #include "pycore_hashtable.h" // export _Py_hashtable_new() | ||
| 49 | 49 | #include "pycore_pyhash.h" // _Py_HashPointerRaw() | |
| 50 | 50 | ||
| 51 | 51 | #define HASHTABLE_MIN_SIZE 16 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -83,8 +83,6 @@ static Py_ssize_t hashstats[Py_HASH_STATS_MAX + 1] = {0}; | |||
| 83 | 83 | ||
| 84 | 84 | */ | |
| 85 | 85 | ||
| 86 | - Py_hash_t _Py_HashPointer(const void *); | ||
| 87 | - | ||
| 88 | 86 | Py_hash_t | |
| 89 | 87 | _Py_HashDouble(PyObject *inst, double v) | |
| 90 | 88 | { | |
@@ -132,23 +130,13 @@ _Py_HashDouble(PyObject *inst, double v) | |||
| 132 | 130 | } | |
| 133 | 131 | ||
| 134 | 132 | Py_hash_t | |
| 135 | - _Py_HashPointerRaw(const void *p) | ||
| 136 | - { | ||
| 137 | - size_t y = (size_t)p; | ||
| 138 | - /* bottom 3 or 4 bits are likely to be 0; rotate y by 4 to avoid | ||
| 139 | - excessive hash collisions for dicts and sets */ | ||
| 140 | - y = (y >> 4) | (y << (8 * SIZEOF_VOID_P - 4)); | ||
| 141 | - return (Py_hash_t)y; | ||
| 142 | - } | ||
| 143 | - | ||
| 144 | - Py_hash_t | ||
| 145 | - _Py_HashPointer(const void *p) | ||
| 133 | + Py_HashPointer(const void *ptr) | ||
| 146 | 134 | { | |
| 147 | - Py_hash_t x = _Py_HashPointerRaw(p); | ||
| 148 | - if (x == -1) { | ||
| 149 | - x = -2; | ||
| 135 | + Py_hash_t hash = _Py_HashPointerRaw(ptr); | ||
| 136 | + if (hash == -1) { | ||
| 137 | + hash = -2; | ||
| 150 | 138 | } | |
| 151 | - return x; | ||
| 139 | + return hash; | ||
| 152 | 140 | } | |
| 153 | 141 | ||
| 154 | 142 | Py_hash_t | |
| Back | FazBrowse Home | New Git URL |
0 commit comments