| /* Common code for use by all hashlib related modules. */ | |
| #include "pycore_lock.h" // PyMutex | |
| /* | |
| * Internal error messages used for reporting an unsupported hash algorithm. | |
| * The algorithm can be given by its name, a callable or a PEP-247 module. | |
| * The same message is raised by Lib/hashlib.py::__get_builtin_constructor() | |
| * and _hmacmodule.c::find_hash_info(). | |
| */ | |
| #define HASHLIB_UNSUPPORTED_ALGORITHM "unsupported hash algorithm %S" | |
| #define HASHLIB_UNSUPPORTED_STR_ALGORITHM "unsupported hash algorithm %s" | |
| /* | |
| * Obtain a buffer view from a buffer-like object 'obj'. | |
| * | |
| * On success, store the result in 'view' and return 0. | |
| * On error, set an exception and return -1. | |
| */ | |
| static inline int | |
| _Py_hashlib_get_buffer_view(PyObject *obj, Py_buffer *view) | |
| { | |
| if (PyUnicode_Check(obj)) { | |
| PyErr_SetString(PyExc_TypeError, | |
| "Strings must be encoded before hashing"); | |
| return -1; | |
| } | |
| if (!PyObject_CheckBuffer(obj)) { | |
| PyErr_SetString(PyExc_TypeError, | |
| "object supporting the buffer API required"); | |
| return -1; | |
| } | |
| if (PyObject_GetBuffer(obj, view, PyBUF_SIMPLE) == -1) { | |
| return -1; | |
| } | |
| if (view->ndim > 1) { | |
| PyErr_SetString(PyExc_BufferError, | |
| "Buffer must be single dimension"); | |
| PyBuffer_Release(view); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| /* | |
| * Call _Py_hashlib_get_buffer_view() and check if it succeeded. | |
| * | |
| * On error, set an exception and execute the ERRACTION statements. | |
| */ | |
| #define GET_BUFFER_VIEW_OR_ERROR(OBJ, VIEW, ERRACTION) \ | |
| do { \ | |
| if (_Py_hashlib_get_buffer_view(OBJ, VIEW) < 0) { \ | |
| assert(PyErr_Occurred()); \ | |
| ERRACTION; \ | |
| } \ | |
| } while (0) | |
| #define GET_BUFFER_VIEW_OR_ERROUT(OBJ, VIEW) \ | |
| GET_BUFFER_VIEW_OR_ERROR(OBJ, VIEW, return NULL) | |
| /* | |
| * Helper code to synchronize access to the hash object when the GIL is | |
| * released around a CPU consuming hashlib operation. | |
| * | |
| * Code accessing a mutable part of the hash object must be enclosed in | |
| * an HASHLIB_{ACQUIRE,RELEASE}_LOCK block or explicitly acquire and release | |
| * the mutex inside a Py_BEGIN_ALLOW_THREADS -- Py_END_ALLOW_THREADS block if | |
| * they wish to release the GIL for an operation. | |
| */ | |
| #define HASHLIB_OBJECT_HEAD \ | |
| PyObject_HEAD \ | |
| /* Guard against race conditions during incremental update(). */ \ | |
| PyMutex mutex; | |
| #define HASHLIB_INIT_MUTEX(OBJ) \ | |
| do { \ | |
| (OBJ)->mutex = (PyMutex){0}; \ | |
| } while (0) | |
| #define HASHLIB_ACQUIRE_LOCK(OBJ) PyMutex_Lock(&(OBJ)->mutex) | |
| #define HASHLIB_RELEASE_LOCK(OBJ) PyMutex_Unlock(&(OBJ)->mutex) | |
| /* | |
| * Message length above which the GIL is to be released | |
| * when performing hashing operations. | |
| */ | |
| #define HASHLIB_GIL_MINSIZE 2048 | |
| // Macros for executing code while conditionally holding the GIL. | |
| // | |
| // These only drop the GIL if the lock acquisition itself is likely to | |
| // block. Thus the non-blocking acquire gating the GIL release for a | |
| // blocking lock acquisition. The intent of these macros is to surround | |
| // the assumed always "fast" operations that you aren't releasing the | |
| // GIL around. | |
| /* | |
| * Execute a suite of C statements 'STATEMENTS'. | |
| * | |
| * The GIL is held if 'SIZE' is below the HASHLIB_GIL_MINSIZE threshold. | |
| */ | |
| #define HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(SIZE, STATEMENTS) \ | |
| do { \ | |
| if ((SIZE) > HASHLIB_GIL_MINSIZE) { \ | |
| Py_BEGIN_ALLOW_THREADS \ | |
| STATEMENTS; \ | |
| Py_END_ALLOW_THREADS \ | |
| } \ | |
| else { \ | |
| STATEMENTS; \ | |
| } \ | |
| } while (0) | |
| /* | |
| * Lock 'OBJ' and execute a suite of C statements 'STATEMENTS'. | |
| * | |
| * The GIL is held if 'SIZE' is below the HASHLIB_GIL_MINSIZE threshold. | |
| */ | |
| #define HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(OBJ, SIZE, STATEMENTS) \ | |
| do { \ | |
| if ((SIZE) > HASHLIB_GIL_MINSIZE) { \ | |
| Py_BEGIN_ALLOW_THREADS \ | |
| HASHLIB_ACQUIRE_LOCK(OBJ); \ | |
| STATEMENTS; \ | |
| HASHLIB_RELEASE_LOCK(OBJ); \ | |
| Py_END_ALLOW_THREADS \ | |
| } \ | |
| else { \ | |
| HASHLIB_ACQUIRE_LOCK(OBJ); \ | |
| STATEMENTS; \ | |
| HASHLIB_RELEASE_LOCK(OBJ); \ | |
| } \ | |
| } while (0) | |
| static inline int | |
| _Py_hashlib_data_argument(PyObject **res, PyObject *data, PyObject *string) | |
| { | |
| if (data != NULL && string == NULL) { | |
| // called as H(data) or H(data=...) | |
| *res = data; | |
| return 1; | |
| } | |
| else if (data == NULL && string != NULL) { | |
| // called as H(string=...) | |
| if (PyErr_WarnEx(PyExc_DeprecationWarning, | |
| "the 'string' keyword parameter is deprecated since " | |
| "Python 3.15 and slated for removal in Python 3.19; " | |
| "use the 'data' keyword parameter or pass the data " | |
| "to hash as a positional argument instead", 1) < 0) | |
| { | |
| *res = NULL; | |
| return -1; | |
| } | |
| *res = string; | |
| return 1; | |
| } | |
| else if (data == NULL && string == NULL) { | |
| // fast path when no data is given | |
| assert(!PyErr_Occurred()); | |
| *res = NULL; | |
| return 0; | |
| } | |
| else { | |
| // called as H(data=..., string) | |
| *res = NULL; | |
| PyErr_SetString(PyExc_TypeError, | |
| "'data' and 'string' are mutually exclusive " | |
| "and support for 'string' keyword parameter " | |
| "is slated for removal in a future version."); | |
| return -1; | |
| } | |
| } |