Skip to content

Commit

Permalink
Merge branch 'main' into deprecate-_PyLong_FromDigits/127937
Browse files Browse the repository at this point in the history
  • Loading branch information
skirpichev authored Jan 24, 2025
2 parents d5e1fef + 3d8fc8b commit 39ed5ea
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 74 deletions.
2 changes: 1 addition & 1 deletion Include/internal/pycore_tracemalloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ struct _tracemalloc_runtime_state {
PyMemAllocatorEx obj;
} allocators;

PyThread_type_lock tables_lock;
PyMutex tables_lock;
/* Size in bytes of currently traced memory.
Protected by TABLES_LOCK(). */
size_t traced_memory;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Convert the :mod:`decimal` module to use :pep:`757` C API (export-import
integers), offering some speed-up if the integer part of the
:class:`~decimal.Decimal` instance is small. Patch by Sergey B Kirpichev.
84 changes: 52 additions & 32 deletions Modules/_decimal/_decimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -2336,15 +2336,16 @@ dec_from_long(decimal_state *state, PyTypeObject *type, PyObject *v,
}
if (export_long.digits) {
const PyLongLayout *layout = PyLong_GetNativeLayout();
uint32_t base = (uint32_t)1 << layout->bits_per_digit;
uint8_t sign = export_long.negative ? MPD_NEG : MPD_POS;
Py_ssize_t len = export_long.ndigits;

assert(layout->bits_per_digit <= 32);
assert(layout->bits_per_digit < 32);
assert(layout->digits_order == -1);
assert(layout->digit_endianness == (PY_LITTLE_ENDIAN ? -1 : 1));
assert(layout->digit_size == 2 || layout->digit_size == 4);

uint32_t base = (uint32_t)1 << layout->bits_per_digit;
uint8_t sign = export_long.negative ? MPD_NEG : MPD_POS;
Py_ssize_t len = export_long.ndigits;

if (layout->digit_size == 4) {
mpd_qimport_u32(MPD(dec), export_long.digits, len, sign,
base, ctx, status);
Expand Down Expand Up @@ -3642,13 +3643,6 @@ dec_format(PyObject *dec, PyObject *args)
static PyObject *
dec_as_long(PyObject *dec, PyObject *context, int round)
{
PyLongObject *pylong;
digit *ob_digit;
size_t n;
mpd_t *x;
mpd_context_t workctx;
uint32_t status = 0;

if (mpd_isspecial(MPD(dec))) {
if (mpd_isnan(MPD(dec))) {
PyErr_SetString(PyExc_ValueError,
Expand All @@ -3661,12 +3655,16 @@ dec_as_long(PyObject *dec, PyObject *context, int round)
return NULL;
}

x = mpd_qnew();
mpd_t *x = mpd_qnew();

if (x == NULL) {
PyErr_NoMemory();
return NULL;
}
workctx = *CTX(context);

mpd_context_t workctx = *CTX(context);
uint32_t status = 0;

workctx.round = round;
mpd_qround_to_int(x, MPD(dec), &workctx, &status);
if (dec_addstatus(context, status)) {
Expand All @@ -3675,34 +3673,56 @@ dec_as_long(PyObject *dec, PyObject *context, int round)
}

status = 0;
ob_digit = NULL;
#if PYLONG_BITS_IN_DIGIT == 30
n = mpd_qexport_u32(&ob_digit, 0, PyLong_BASE, x, &status);
#elif PYLONG_BITS_IN_DIGIT == 15
n = mpd_qexport_u16(&ob_digit, 0, PyLong_BASE, x, &status);
#else
#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"
#endif
int64_t val = mpd_qget_i64(x, &status);

if (!status) {
mpd_del(x);
return PyLong_FromInt64(val);
}
assert(!mpd_iszero(x));

const PyLongLayout *layout = PyLong_GetNativeLayout();

assert(layout->bits_per_digit < 32);
assert(layout->digits_order == -1);
assert(layout->digit_endianness == (PY_LITTLE_ENDIAN ? -1 : 1));
assert(layout->digit_size == 2 || layout->digit_size == 4);

uint32_t base = (uint32_t)1 << layout->bits_per_digit;
/* We use a temporary buffer for digits for now, as for nonzero rdata
mpd_qexport_u32/u16() require either space "allocated by one of
libmpdec’s allocation functions" or "rlen MUST be correct" (to avoid
reallocation). This can be further optimized by using rlen from
mpd_sizeinbase(). See gh-127925. */
void *tmp_digits = NULL;
size_t n;

status = 0;
if (layout->digit_size == 4) {
n = mpd_qexport_u32((uint32_t **)&tmp_digits, 0, base, x, &status);
}
else {
n = mpd_qexport_u16((uint16_t **)&tmp_digits, 0, base, x, &status);
}

if (n == SIZE_MAX) {
PyErr_NoMemory();
mpd_del(x);
mpd_free(tmp_digits);
return NULL;
}

if (n == 1) {
sdigit val = mpd_arith_sign(x) * ob_digit[0];
mpd_free(ob_digit);
mpd_del(x);
return PyLong_FromLong(val);
}
void *digits;
PyLongWriter *writer = PyLongWriter_Create(mpd_isnegative(x), n, &digits);

assert(n > 0);
assert(!mpd_iszero(x));
pylong = _PyLong_FromDigits(mpd_isnegative(x), n, ob_digit);
mpd_free(ob_digit);
mpd_del(x);
return (PyObject *) pylong;
if (writer == NULL) {
mpd_free(tmp_digits);
return NULL;
}
memcpy(digits, tmp_digits, layout->digit_size*n);
mpd_free(tmp_digits);
return PyLongWriter_Finish(writer);
}

/* Convert a Decimal to its exact integer ratio representation. */
Expand Down
24 changes: 16 additions & 8 deletions Modules/_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,13 @@ _build_rval_index_tuple(PyObject *rval, Py_ssize_t idx) {
return tpl;
}

static inline int
_PyUnicodeWriter_IsEmpty(PyUnicodeWriter *writer_pub)
{
_PyUnicodeWriter *writer = (_PyUnicodeWriter*)writer_pub;
return (writer->pos == 0);
}

static PyObject *
scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next_end_ptr)
{
Expand All @@ -371,9 +378,10 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next
const void *buf;
int kind;

_PyUnicodeWriter writer;
_PyUnicodeWriter_Init(&writer);
writer.overallocate = 1;
PyUnicodeWriter *writer = PyUnicodeWriter_Create(0);
if (writer == NULL) {
goto bail;
}

len = PyUnicode_GET_LENGTH(pystr);
buf = PyUnicode_DATA(pystr);
Expand Down Expand Up @@ -404,7 +412,7 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next

if (c == '"') {
// Fast path for simple case.
if (writer.buffer == NULL) {
if (_PyUnicodeWriter_IsEmpty(writer)) {
PyObject *ret = PyUnicode_Substring(pystr, end, next);
if (ret == NULL) {
goto bail;
Expand All @@ -420,7 +428,7 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next

/* Pick up this chunk if it's not zero length */
if (next != end) {
if (_PyUnicodeWriter_WriteSubstring(&writer, pystr, end, next) < 0) {
if (PyUnicodeWriter_WriteSubstring(writer, pystr, end, next) < 0) {
goto bail;
}
}
Expand Down Expand Up @@ -511,18 +519,18 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next
end -= 6;
}
}
if (_PyUnicodeWriter_WriteChar(&writer, c) < 0) {
if (PyUnicodeWriter_WriteChar(writer, c) < 0) {
goto bail;
}
}

rval = _PyUnicodeWriter_Finish(&writer);
rval = PyUnicodeWriter_Finish(writer);
*next_end_ptr = end;
return rval;

bail:
*next_end_ptr = -1;
_PyUnicodeWriter_Dealloc(&writer);
PyUnicodeWriter_Discard(writer);
return NULL;
}

Expand Down
62 changes: 43 additions & 19 deletions Python/codecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -659,44 +659,64 @@ PyObject *PyCodec_LookupError(const char *name)
return handler;
}

static void wrong_exception_type(PyObject *exc)

static inline void
wrong_exception_type(PyObject *exc)
{
PyErr_Format(PyExc_TypeError,
"don't know how to handle %.200s in error callback",
Py_TYPE(exc)->tp_name);
"don't know how to handle %T in error callback", exc);
}


#define _PyIsUnicodeEncodeError(EXC) \
PyObject_TypeCheck(EXC, (PyTypeObject *)PyExc_UnicodeEncodeError)
#define _PyIsUnicodeDecodeError(EXC) \
PyObject_TypeCheck(EXC, (PyTypeObject *)PyExc_UnicodeDecodeError)
#define _PyIsUnicodeTranslateError(EXC) \
PyObject_TypeCheck(EXC, (PyTypeObject *)PyExc_UnicodeTranslateError)


// --- handler: 'strict' ------------------------------------------------------

PyObject *PyCodec_StrictErrors(PyObject *exc)
{
if (PyExceptionInstance_Check(exc))
if (PyExceptionInstance_Check(exc)) {
PyErr_SetObject(PyExceptionInstance_Class(exc), exc);
else
}
else {
PyErr_SetString(PyExc_TypeError, "codec must pass exception instance");
}
return NULL;
}


PyObject *PyCodec_IgnoreErrors(PyObject *exc)
// --- handler: 'ignore' ------------------------------------------------------

static PyObject *
_PyCodec_IgnoreError(PyObject *exc, int as_bytes)
{
Py_ssize_t end;

if (PyObject_TypeCheck(exc, (PyTypeObject *)PyExc_UnicodeEncodeError)) {
if (PyUnicodeEncodeError_GetEnd(exc, &end))
return NULL;
if (_PyUnicodeError_GetParams(exc, NULL, NULL, NULL,
&end, NULL, as_bytes) < 0)
{
return NULL;
}
else if (PyObject_TypeCheck(exc, (PyTypeObject *)PyExc_UnicodeDecodeError)) {
if (PyUnicodeDecodeError_GetEnd(exc, &end))
return NULL;
return Py_BuildValue("(Nn)", Py_GetConstant(Py_CONSTANT_EMPTY_STR), end);
}


PyObject *PyCodec_IgnoreErrors(PyObject *exc)
{
if (_PyIsUnicodeEncodeError(exc) || _PyIsUnicodeTranslateError(exc)) {
return _PyCodec_IgnoreError(exc, false);
}
else if (PyObject_TypeCheck(exc, (PyTypeObject *)PyExc_UnicodeTranslateError)) {
if (PyUnicodeTranslateError_GetEnd(exc, &end))
return NULL;
else if (_PyIsUnicodeDecodeError(exc)) {
return _PyCodec_IgnoreError(exc, true);
}
else {
wrong_exception_type(exc);
return NULL;
}
return Py_BuildValue("(Nn)", Py_GetConstant(Py_CONSTANT_EMPTY_STR), end);
}


Expand Down Expand Up @@ -1368,13 +1388,17 @@ PyCodec_SurrogateEscapeErrors(PyObject *exc)
}


static PyObject *strict_errors(PyObject *self, PyObject *exc)
// --- Codecs registry handlers -----------------------------------------------

static inline PyObject *
strict_errors(PyObject *Py_UNUSED(self), PyObject *exc)
{
return PyCodec_StrictErrors(exc);
}


static PyObject *ignore_errors(PyObject *self, PyObject *exc)
static inline PyObject *
ignore_errors(PyObject *Py_UNUSED(self), PyObject *exc)
{
return PyCodec_IgnoreErrors(exc);
}
Expand Down
17 changes: 3 additions & 14 deletions Python/tracemalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "pycore_gc.h" // PyGC_Head
#include "pycore_hashtable.h" // _Py_hashtable_t
#include "pycore_initconfig.h" // _PyStatus_NO_MEMORY()
#include "pycore_lock.h" // PyMutex_LockFlags()
#include "pycore_object.h" // _PyType_PreHeaderSize()
#include "pycore_pymem.h" // _Py_tracemalloc_config
#include "pycore_runtime.h" // _Py_ID()
Expand Down Expand Up @@ -37,8 +38,8 @@ static int _PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event,
the GIL held from PyMem_RawFree(). It cannot acquire the lock because it
would introduce a deadlock in _PyThreadState_DeleteCurrent(). */
#define tables_lock _PyRuntime.tracemalloc.tables_lock
#define TABLES_LOCK() PyThread_acquire_lock(tables_lock, 1)
#define TABLES_UNLOCK() PyThread_release_lock(tables_lock)
#define TABLES_LOCK() PyMutex_LockFlags(&tables_lock, _Py_LOCK_DONT_DETACH)
#define TABLES_UNLOCK() PyMutex_Unlock(&tables_lock)


#define DEFAULT_DOMAIN 0
Expand Down Expand Up @@ -741,13 +742,6 @@ _PyTraceMalloc_Init(void)
return _PyStatus_NO_MEMORY();
}

if (tables_lock == NULL) {
tables_lock = PyThread_allocate_lock();
if (tables_lock == NULL) {
return _PyStatus_NO_MEMORY();
}
}

tracemalloc_filenames = hashtable_new(hashtable_hash_pyobject,
hashtable_compare_unicode,
tracemalloc_clear_filename, NULL);
Expand Down Expand Up @@ -792,11 +786,6 @@ tracemalloc_deinit(void)
_Py_hashtable_destroy(tracemalloc_tracebacks);
_Py_hashtable_destroy(tracemalloc_filenames);

if (tables_lock != NULL) {
PyThread_free_lock(tables_lock);
tables_lock = NULL;
}

PyThread_tss_delete(&tracemalloc_reentrant_key);
}

Expand Down

0 comments on commit 39ed5ea

Please sign in to comment.