Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Include/internal/pycore_floatobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ extern double _Py_parse_inf_or_nan(const char *p, char **endptr);

extern int _Py_convert_int_to_double(PyObject **v, double *dbl);

// Export for '_json' shared extension.
PyAPI_FUNC(PyObject*) _PyFloat_FromString(const char *str, Py_ssize_t len);

/* Should match endianness of the platform in most (all?) cases. */

#ifdef DOUBLE_IS_BIG_ENDIAN_IEEE754
Expand Down
41 changes: 24 additions & 17 deletions Modules/_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST()
#include "pycore_dict.h" // _PyDict_SetItem_Take2()
#include "pycore_floatobject.h" // _PyFloat_FromString()
#include "pycore_list.h" // _PyList_AppendTakeRef()
#include "pycore_global_strings.h" // _Py_ID()
#include "pycore_pyerrors.h" // _PyErr_FormatNote
Expand Down Expand Up @@ -997,7 +998,6 @@ _match_number_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t start, Py_
Py_ssize_t idx = start;
int is_float = 0;
PyObject *rval;
PyObject *numstr = NULL;
PyObject *custom_func;

str = PyUnicode_DATA(pystr);
Expand Down Expand Up @@ -1064,32 +1064,39 @@ _match_number_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t start, Py_

if (custom_func) {
/* copy the section we determined to be a number */
numstr = PyUnicode_FromKindAndData(kind,
(char*)str + kind * start,
idx - start);
if (numstr == NULL)
PyObject *numstr = PyUnicode_FromKindAndData(kind,
(char*)str + kind * start,
idx - start);
if (numstr == NULL) {
return NULL;
}
rval = PyObject_CallOneArg(custom_func, numstr);
Py_DECREF(numstr);
}
else {
Py_ssize_t i, n;
char *buf;
/* Straight conversion to ASCII, to avoid costly conversion of
decimal unicode digits (which cannot appear here) */
n = idx - start;
numstr = PyBytes_FromStringAndSize(NULL, n);
if (numstr == NULL)
Py_ssize_t n = idx - start;
char *buf = PyMem_Malloc(n + 1);
if (buf == NULL) {
PyErr_NoMemory();
return NULL;
buf = PyBytes_AS_STRING(numstr);
for (i = 0; i < n; i++) {
buf[i] = (char) PyUnicode_READ(kind, str, i + start);
}
if (is_float)
rval = PyFloat_FromString(numstr);
else

for (Py_ssize_t i = 0; i < n; i++) {
Py_UCS4 ch = PyUnicode_READ(kind, str, i + start);
assert(ch <= 127);
buf[i] = (char)ch;
}
buf[n] = '\0';
if (is_float) {
rval = _PyFloat_FromString(buf, n);
}
else {
rval = PyLong_FromString(buf, NULL, 10);
}
PyMem_Free(buf);
}
Py_DECREF(numstr);
*next_idx_ptr = idx;
return rval;
}
Expand Down
46 changes: 37 additions & 9 deletions Objects/floatobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,20 +137,18 @@ PyFloat_FromDouble(double fval)
}

static PyObject *
float_from_string_inner(const char *s, Py_ssize_t len, void *obj)
float_from_string_inner(const char *s, Py_ssize_t len, void *data)
{
double x;
const char *orig_s = s;
const char *end;
const char *last = s + len;
/* strip leading whitespace */
while (s < last && Py_ISSPACE(*s)) {
s++;
}
if (s == last) {
PyErr_Format(PyExc_ValueError,
"could not convert string to float: "
"%R", obj);
return NULL;
goto error;
}

/* strip trailing whitespace */
Expand All @@ -163,19 +161,49 @@ float_from_string_inner(const char *s, Py_ssize_t len, void *obj)
* fine. */
x = PyOS_string_to_double(s, (char **)&end, NULL);
if (end != last) {
PyErr_Format(PyExc_ValueError,
"could not convert string to float: "
"%R", obj);
return NULL;
goto error;
}
else if (x == -1.0 && PyErr_Occurred()) {
return NULL;
}
else {
return PyFloat_FromDouble(x);
}

error:
// Label followed by a declaration is a C23 extension, so use a sub-scope
{
PyObject *obj = (PyObject*)data;
if (obj == NULL) {
obj = PyBytes_FromStringAndSize(orig_s, len);
if (obj == NULL) {
return NULL;
}
}
else {
Py_INCREF(obj);
}
PyErr_Format(PyExc_ValueError,
"could not convert string to float: "
"%R", obj);
Py_DECREF(obj);
}
return NULL;
}


// Internal API similar to PyFloat_FromString() but doesn't require a Python
// object. str[len] must be the NUL byte.
PyObject*
_PyFloat_FromString(const char *str, Py_ssize_t len)
{
assert(len >= 1);
assert(str[len] == '\0');
return _Py_string_to_number_with_underscores(str, len, "float", NULL, NULL,
float_from_string_inner);
}


PyObject *
PyFloat_FromString(PyObject *v)
{
Expand Down
10 changes: 10 additions & 0 deletions Python/pystrtod.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,19 @@ _Py_string_to_number_with_underscores(

error:
PyMem_Free(dup);
if (obj == NULL) {
obj = PyBytes_FromStringAndSize(s, orig_len);
if (obj == NULL) {
return NULL;
}
}
else {
Py_INCREF(obj);
}
PyErr_Format(PyExc_ValueError,
"could not convert string to %s: "
"%R", what, obj);
Py_DECREF(obj);
return NULL;
}

Expand Down
Loading