mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	
		
			
	
	
		
			174 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			174 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /* 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; | ||
|  |     } | ||
|  | } |