Commit 9e37fb79 authored by Stefan Behnel's avatar Stefan Behnel

avoid intermediate PyInt/Long creation when formatting C integer values

parent f7f6dd3d
...@@ -3028,9 +3028,12 @@ class FormattedValueNode(ExprNode): ...@@ -3028,9 +3028,12 @@ class FormattedValueNode(ExprNode):
return False return False
def analyse_types(self, env): def analyse_types(self, env):
self.value = self.value.analyse_types(env).coerce_to_pyobject(env) self.value = self.value.analyse_types(env)
if self.format_spec: if self.format_spec:
self.format_spec = self.format_spec.analyse_types(env).coerce_to_pyobject(env) self.format_spec = self.format_spec.analyse_types(env).coerce_to_pyobject(env)
elif not self.conversion_char and self.value.type.can_coerce_to_pyunicode(env):
return FormattedCValueNode(self.pos, value=self.value)
self.value = self.value.coerce_to_pyobject(env)
return self return self
def generate_result_code(self, code): def generate_result_code(self, code):
...@@ -3064,6 +3067,20 @@ class FormattedValueNode(ExprNode): ...@@ -3064,6 +3067,20 @@ class FormattedValueNode(ExprNode):
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
class FormattedCValueNode(FormattedValueNode):
conversion_char = None
format_spec = None
def generate_result_code(self, code):
convert_func = self.value.type.to_pyunicode_utility_code(code)
code.putln("%s = %s(%s); %s" % (
self.result(),
convert_func,
self.value.result(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
#------------------------------------------------------------------- #-------------------------------------------------------------------
# #
# Parallel nodes (cython.parallel.thread(savailable|id)) # Parallel nodes (cython.parallel.thread(savailable|id))
......
...@@ -30,6 +30,9 @@ class BaseType(object): ...@@ -30,6 +30,9 @@ class BaseType(object):
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
return False return False
def can_coerce_to_pyunicode(self, env):
return False
def cast_code(self, expr_code): def cast_code(self, expr_code):
return "((%s)%s)" % (self.empty_declaration_code(), expr_code) return "((%s)%s)" % (self.empty_declaration_code(), expr_code)
...@@ -1620,11 +1623,28 @@ class CIntType(CNumericType): ...@@ -1620,11 +1623,28 @@ class CIntType(CNumericType):
typedef_flag = 0 typedef_flag = 0
to_py_function = None to_py_function = None
from_py_function = None from_py_function = None
to_pyunicode_utility = None
exception_value = -1 exception_value = -1
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
return True return True
def can_coerce_to_pyunicode(self, env):
return True
def to_pyunicode_utility_code(self, code):
if self.to_pyunicode_utility is None:
utility_code_name = "__Pyx_PyUnicode_From_" + self.specialization_name()
to_pyunicode_utility = TempitaUtilityCode.load_cached(
"CIntToPyUnicode", "TypeConversion.c",
context={"TYPE": self.empty_declaration_code(),
"TO_PY_FUNCTION": utility_code_name})
self.to_pyunicode_utility = (utility_code_name, to_pyunicode_utility)
else:
utility_code_name, to_pyunicode_utility = self.to_pyunicode_utility
code.globalstate.use_utility_code(to_pyunicode_utility)
return utility_code_name
def create_to_py_utility_code(self, env): def create_to_py_utility_code(self, env):
if type(self).to_py_function is None: if type(self).to_py_function is None:
self.to_py_function = "__Pyx_PyInt_From_" + self.specialization_name() self.to_py_function = "__Pyx_PyInt_From_" + self.specialization_name()
...@@ -1732,6 +1752,9 @@ class CReturnCodeType(CIntType): ...@@ -1732,6 +1752,9 @@ class CReturnCodeType(CIntType):
is_returncode = True is_returncode = True
exception_check = False exception_check = False
def can_coerce_to_pyunicode(self, env):
return False
class CBIntType(CIntType): class CBIntType(CIntType):
...@@ -1739,6 +1762,9 @@ class CBIntType(CIntType): ...@@ -1739,6 +1762,9 @@ class CBIntType(CIntType):
from_py_function = "__Pyx_PyObject_IsTrue" from_py_function = "__Pyx_PyObject_IsTrue"
exception_check = 1 # for C++ bool exception_check = 1 # for C++ bool
def can_coerce_to_pyunicode(self, env):
return False
def declaration_code(self, entity_code, def declaration_code(self, entity_code,
for_display = 0, dll_linkage = None, pyrex = 0): for_display = 0, dll_linkage = None, pyrex = 0):
if for_display: if for_display:
...@@ -1773,6 +1799,9 @@ class CPyUCS4IntType(CIntType): ...@@ -1773,6 +1799,9 @@ class CPyUCS4IntType(CIntType):
to_py_function = "PyUnicode_FromOrdinal" to_py_function = "PyUnicode_FromOrdinal"
from_py_function = "__Pyx_PyObject_AsPy_UCS4" from_py_function = "__Pyx_PyObject_AsPy_UCS4"
def can_coerce_to_pyunicode(self, env):
return False
def create_from_py_utility_code(self, env): def create_from_py_utility_code(self, env):
env.use_utility_code(UtilityCode.load_cached("ObjectAsUCS4", "TypeConversion.c")) env.use_utility_code(UtilityCode.load_cached("ObjectAsUCS4", "TypeConversion.c"))
return True return True
...@@ -1794,6 +1823,9 @@ class CPyUnicodeIntType(CIntType): ...@@ -1794,6 +1823,9 @@ class CPyUnicodeIntType(CIntType):
to_py_function = "PyUnicode_FromOrdinal" to_py_function = "PyUnicode_FromOrdinal"
from_py_function = "__Pyx_PyObject_AsPy_UNICODE" from_py_function = "__Pyx_PyObject_AsPy_UNICODE"
def can_coerce_to_pyunicode(self, env):
return False
def create_from_py_utility_code(self, env): def create_from_py_utility_code(self, env):
env.use_utility_code(UtilityCode.load_cached("ObjectAsPyUnicode", "TypeConversion.c")) env.use_utility_code(UtilityCode.load_cached("ObjectAsPyUnicode", "TypeConversion.c"))
return True return True
......
...@@ -568,6 +568,68 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) { ...@@ -568,6 +568,68 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) {
} }
/////////////// CIntToPyUnicode.proto ///////////////
static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value);
/////////////// CIntToPyUnicode ///////////////
static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) {
// simple and conservative string allocation on the stack
char digits[sizeof({{TYPE}})*3+2];
int length = -1;
const {{TYPE}} neg_one = ({{TYPE}}) -1, const_zero = ({{TYPE}}) 0;
const int is_unsigned = neg_one > const_zero;
if (is_unsigned) {
if (sizeof({{TYPE}}) <= sizeof(unsigned int)) {
length = sprintf(digits, "%u", (unsigned int) value);
} else if (sizeof({{TYPE}}) <= sizeof(unsigned long)) {
length = sprintf(digits, "%lu", (unsigned long) value);
} else if (sizeof({{TYPE}}) <= sizeof(unsigned PY_LONG_LONG)) {
length = sprintf(digits, "%llu", (unsigned PY_LONG_LONG) value);
}
} else {
if (sizeof({{TYPE}}) <= sizeof(int)) {
length = sprintf(digits, "%d", (int) value);
} else if (sizeof({{TYPE}}) <= sizeof(long)) {
length = sprintf(digits, "%ld", (long) value);
} else if (sizeof({{TYPE}}) <= sizeof(PY_LONG_LONG)) {
length = sprintf(digits, "%lld", (PY_LONG_LONG) value);
}
}
if (unlikely(length < 0)) {
// huge integer type or (unlikely) error in sprintf() => use slow conversion
PyObject *pylong, *uval = NULL;
int one = 1; int little = (int)*(unsigned char *)&one;
unsigned char *bytes = (unsigned char *)&value;
pylong = _PyLong_FromByteArray(bytes, sizeof({{TYPE}}), little, !is_unsigned);
if (likely(pylong)) {
#if PY_MAJOR_VERSION >= 3
uval = PyObject_Str(pylong);
#else
uval = PyObject_Unicode(pylong);
#endif
Py_DECREF(pylong);
}
return uval;
}
return PyUnicode_DecodeASCII(digits, length, NULL);
}
/////////////// NewOwnedRef.proto ///////////////
static CYTHON_INLINE PyObject* __Pyx_NewOwnedRef(PyObject* value);
/////////////// NewOwnedRef ///////////////
static CYTHON_INLINE PyObject* __Pyx_NewOwnedRef(PyObject* value) {
Py_INCREF(value);
return value;
}
/////////////// PyIntFromDouble.proto /////////////// /////////////// PyIntFromDouble.proto ///////////////
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
......
...@@ -32,11 +32,11 @@ def format2(ab, cd): ...@@ -32,11 +32,11 @@ def format2(ab, cd):
return a, b, c return a, b, c
def format_c_numbers(int n, float f, double d): def format_c_numbers(char c, short s, int n, long l, float f, double d):
""" """
>>> s1, s2, s3, s4 = format_c_numbers(12, 2.3456, 3.1415926) >>> s1, s2, s3, s4 = format_c_numbers(123, 135, 12, 12312312, 2.3456, 3.1415926)
>>> print(s1) >>> print(s1)
122.35 123 13512312312122.35
>>> print(s2) >>> print(s2)
3.14 2.3 3.14 2.3
>>> print(s3) >>> print(s3)
...@@ -45,7 +45,7 @@ def format_c_numbers(int n, float f, double d): ...@@ -45,7 +45,7 @@ def format_c_numbers(int n, float f, double d):
C 3.14 C 3.14
""" """
s1 = f"{n}{f:.3}" s1 = f"{c}{s:4}{l}{n}{f:.3}"
assert isinstance(s1, unicode), type(s1) assert isinstance(s1, unicode), type(s1)
s2 = f"{d:.3}{f:4.2}" s2 = f"{d:.3}{f:4.2}"
assert isinstance(s2, unicode), type(s2) assert isinstance(s2, unicode), type(s2)
...@@ -56,6 +56,26 @@ def format_c_numbers(int n, float f, double d): ...@@ -56,6 +56,26 @@ def format_c_numbers(int n, float f, double d):
return s1, s2, s3, s4 return s1, s2, s3, s4
def format_c_values(Py_UCS4 uchar, Py_UNICODE pyunicode):
"""
>>> s, s1, s2 = format_c_values(b'A'.decode('ascii'), b'X'.decode('ascii'))
>>> print(s)
AXAX
>>> print(s1)
A
>>> print(s2)
X
"""
s = f"{uchar}{pyunicode}{uchar!s}{pyunicode!s}"
assert isinstance(s, unicode), type(s)
s1 = f"{uchar}"
assert isinstance(s1, unicode), type(s1)
s2 = f"{pyunicode}"
assert isinstance(s2, unicode), type(s2)
return s, s1, s2
def format_strings(str s, unicode u): def format_strings(str s, unicode u):
""" """
>>> a, b, c, d = format_strings('abc', b'xyz'.decode('ascii')) >>> a, b, c, d = format_strings('abc', b'xyz'.decode('ascii'))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment