diff --git a/Doc/library/array.rst b/Doc/library/array.rst index 4468edb6efa..3cd10746059 100644 --- a/Doc/library/array.rst +++ b/Doc/library/array.rst @@ -52,6 +52,10 @@ defined: +-----------+--------------------+-------------------+-----------------------+-------+ | ``'D'`` | double complex | complex | 16 | \(4) | +-----------+--------------------+-------------------+-----------------------+-------+ +| ``'Zf'`` | float complex | complex | 8 | \(4) | ++-----------+--------------------+-------------------+-----------------------+-------+ +| ``'Zd'`` | double complex | complex | 16 | \(4) | ++-----------+--------------------+-------------------+-----------------------+-------+ Notes: @@ -80,7 +84,7 @@ Notes: .. versionadded:: 3.15 (4) - Complex types (``F`` and ``D``) are available unconditionally, + Complex types (``F``, ``D``, ``Zf`` and ``Zd``) are available unconditionally, regardless on support for complex types (the Annex G of the C11 standard) by the C compiler. As specified in the C11 standard, each complex type is represented by a @@ -105,7 +109,10 @@ The module defines the following item: .. data:: typecodes - A string with all available type codes. + A tuple with all available type codes. + + .. versionchanged:: next + The type changed from :class:`str` to :class:`tuple`. The module defines the following type: diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index b8d615565a5..3db66ad8ea6 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -370,15 +370,19 @@ in both C and ``libffi``, the following complex types are available: * - :class:`c_float_complex` - :c:expr:`float complex` - :py:class:`complex` - - ``'F'`` + - ``'Zf'`` * - :class:`c_double_complex` - :c:expr:`double complex` - :py:class:`complex` - - ``'D'`` + - ``'Zd'`` * - :class:`c_longdouble_complex` - :c:expr:`long double complex` - :py:class:`complex` - - ``'G'`` + - ``'Zg'`` + +.. versionchanged:: next + The :py:attr:`~_SimpleCData._type_` types ``F``, ``D`` and ``G`` have been + replaced with ``Zf``, ``Zd`` and ``Zg``. All these types can be created by calling them with an optional initializer of diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst index f504f931f0f..bf5f754e156 100644 --- a/Doc/library/struct.rst +++ b/Doc/library/struct.rst @@ -264,6 +264,10 @@ platform-dependent. +--------+--------------------------+--------------------+----------------+------------+ | ``D`` | :c:expr:`double complex` | complex | 16 | \(10) | +--------+--------------------------+--------------------+----------------+------------+ +| ``Zf`` | :c:expr:`float complex` | complex | 8 | \(10) | ++--------+--------------------------+--------------------+----------------+------------+ +| ``Zd`` | :c:expr:`double complex` | complex | 16 | \(10) | ++--------+--------------------------+--------------------+----------------+------------+ | ``s`` | :c:expr:`char[]` | bytes | | \(9) | +--------+--------------------------+--------------------+----------------+------------+ | ``p`` | :c:expr:`char[]` | bytes | | \(8) | @@ -280,6 +284,9 @@ platform-dependent. .. versionchanged:: 3.14 Added support for the ``'F'`` and ``'D'`` formats. +.. versionchanged:: next + Added support for the ``'Zf'`` and ``'Zd'`` formats. + .. seealso:: The :mod:`array` and :ref:`ctypes ` modules, @@ -372,7 +379,7 @@ Notes: For the ``'F'`` and ``'D'`` format characters, the packed representation uses the IEEE 754 binary32 and binary64 format for components of the complex number, regardless of the floating-point format used by the platform. - Note that complex types (``F`` and ``D``) are available unconditionally, + Note that complex types (``F``/``Zf`` and ``D``/``Zd``) are available unconditionally, despite complex types being an optional feature in C. As specified in the C11 standard, each complex type is represented by a two-element C array containing, respectively, the real and imaginary parts. diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 53628b2ff46..c006ea3a50d 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -668,9 +668,10 @@ Other language changes (Contributed by James Hilton-Balfe in :gh:`128335`.) * The class :class:`memoryview` now supports the :c:expr:`float complex` and - :c:expr:`double complex` C types: formatting characters ``'F'`` and ``'D'`` - respectively. - (Contributed by Sergey B Kirpichev in :gh:`146151`.) + :c:expr:`double complex` C types: formatting characters ``'F'``/``'Zf'`` + and ``'D'``/``'Zd'`` respectively. + (Contributed by Sergey B Kirpichev and Victor Stinner in :gh:`146151` + and :gh:`148675`.) * Allow the *count* argument of :meth:`bytes.replace` to be a keyword. (Contributed by Stan Ulbrych in :gh:`147856`.) @@ -721,13 +722,18 @@ array ----- * Support the :c:expr:`float complex` and :c:expr:`double complex` C types: - formatting characters ``'F'`` and ``'D'`` respectively. - (Contributed by Sergey B Kirpichev in :gh:`146151`.) + formatting characters ``'F'``/``'Zd'`` and ``'D'``/``'Zd'`` respectively. + (Contributed by Sergey B Kirpichev and Victor Stinner in :gh:`146151` + and :gh:`148675`.) * Support half-floats (16-bit IEEE 754 binary interchange format): formatting character ``'e'``. (Contributed by Sergey B Kirpichev in :gh:`146238`.) +* The :data:`array.typecodes` type changed from :class:`str` to :class:`tuple` + to support type codes longer than 1 character (``Zf`` and ``Zd``). + (Contributed by Victor Stinner in :gh:`148675`.) + ast --- @@ -1733,6 +1739,12 @@ ctypes which has been deprecated since Python 3.13. (Contributed by Bénédikt Tran in :gh:`133866`.) +* Change the :py:attr:`~ctypes._SimpleCData._type_` of + :class:`~ctypes.c_float_complex`, :class:`~ctypes.c_double_complex` and + :class:`~ctypes.c_longdouble_complex` from ``F``, ``D`` and ``G`` to ``Zf``, + ``Zd`` and ``Zg`` for compatibility with numpy. + (Contributed by Victor Stinner in :gh:`148675`.) + datetime -------- diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 6d7cb56f6c3..0b84a27f8c6 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -206,13 +206,13 @@ class c_longdouble(_SimpleCData): try: class c_double_complex(_SimpleCData): - _type_ = "D" + _type_ = "Zd" _check_size(c_double_complex) class c_float_complex(_SimpleCData): - _type_ = "F" + _type_ = "Zf" _check_size(c_float_complex) class c_longdouble_complex(_SimpleCData): - _type_ = "G" + _type_ = "Zg" except AttributeError: pass diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 4493349798d..54ecc886917 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -31,7 +31,10 @@ class ArraySubclassWithKwargs(array.array): def __init__(self, typecode, newarg=None): array.array.__init__(self) -typecodes = 'uwbBhHiIlLfdqQFDe' +typecodes = ( + 'u', 'w', 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', + 'f', 'd', 'q', 'Q', 'F', 'D', 'e', 'Zf', 'Zd') + class MiscTest(unittest.TestCase): @@ -42,8 +45,9 @@ def test_array_is_sequence(self): def test_bad_constructor(self): self.assertRaises(TypeError, array.array) self.assertRaises(TypeError, array.array, spam=42) - self.assertRaises(TypeError, array.array, 'xx') + self.assertRaises(ValueError, array.array, 'xx') self.assertRaises(ValueError, array.array, 'x') + self.assertRaises(ValueError, array.array, 'Z') @support.cpython_only def test_disallow_instantiation(self): @@ -85,6 +89,12 @@ def __index__(self): with self.assertRaises(TypeError): a.fromlist(lst) + def test_typecodes(self): + self.assertIsInstance(array.typecodes, tuple) + for typecode in array.typecodes: + self.assertIsInstance(typecode, str) + self.assertGreaterEqual(len(typecode), 1) + # Machine format codes. # @@ -208,6 +218,14 @@ def test_numbers(self): [9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]), (['D'], IEEE_754_DOUBLE_COMPLEX_BE, '>DDDD', [9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]), + (['Zf'], IEEE_754_FLOAT_COMPLEX_LE, 'ZfZfZfZf', + [16711938.0j, float('inf'), complex('1-infj'), -0.0]), + (['Zd'], IEEE_754_DOUBLE_COMPLEX_LE, 'ZdZdZdZd', + [9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]), ) for testcase in testcases: valid_typecodes, mformat_code, struct_fmt, values = testcase @@ -1237,6 +1255,9 @@ def test_free_after_iterating(self): support.check_free_after_iterating(self, reversed, array.array, (self.typecode,)) + def test_known_typecode(self): + self.assertIn(self.typecode, array.typecodes) + class StringTest(BaseTest): def test_setitem(self): @@ -1576,7 +1597,7 @@ def test_nan(self): def test_byteswap(self): a = array.array(self.typecode, self.example) self.assertRaises(TypeError, a.byteswap, 42) - if a.itemsize in (1, 2, 4, 8): + if a.itemsize in (1, 2, 4, 8, 16): b = array.array(self.typecode, self.example) b.byteswap() if a.itemsize == 1: @@ -1631,6 +1652,14 @@ class ComplexDoubleTest(CFPTest, unittest.TestCase): typecode = 'D' minitemsize = 16 +class ComplexZfFloatTest(CFPTest, unittest.TestCase): + typecode = 'Zf' + minitemsize = 8 + +class ComplexZdDoubleTest(CFPTest, unittest.TestCase): + typecode = 'Zd' + minitemsize = 16 + class LargeArrayTest(unittest.TestCase): typecode = 'b' diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 5a4f031e298..21a3d0b3422 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -67,7 +67,7 @@ 'h':0, 'H':0, 'i':0, 'I':0, 'l':0, 'L':0, 'n':0, 'N':0, 'e':0, 'f':0, 'd':0, 'P':0, - 'F':0, 'D':0 + 'F':0, 'D':0, 'Zf':0, 'Zd':0, } # NumPy does not have 'n' or 'N': @@ -95,7 +95,9 @@ 'e':(-65519, 65520), 'f':(-(1<<63), 1<<63), 'd':(-(1<<1023), 1<<1023), 'F':(-(1<<63), 1<<63), - 'D':(-(1<<1023), 1<<1023) + 'D':(-(1<<1023), 1<<1023), + 'Zf':(-(1<<63), 1<<63), + 'Zd':(-(1<<1023), 1<<1023), } def native_type_range(fmt): @@ -110,9 +112,9 @@ def native_type_range(fmt): lh = (-(1<<63), 1<<63) elif fmt == 'd': lh = (-(1<<1023), 1<<1023) - elif fmt == 'F': + elif fmt in ('F', 'Zf'): lh = (-(1<<63), 1<<63) - elif fmt == 'D': + elif fmt in ('D', 'Zd'): lh = (-(1<<1023), 1<<1023) else: for exp in (128, 127, 64, 63, 32, 31, 16, 15, 8, 7): @@ -182,18 +184,28 @@ def randrange_fmt(mode, char, obj): if char in 'efd': x = struct.pack(char, x) x = struct.unpack(char, x)[0] - if char in 'FD': + if char in ('F', 'D', 'Zf', 'Zd'): y = randrange(*fmtdict[mode][char]) x = complex(x, y) x = struct.pack(char, x) x = struct.unpack(char, x)[0] return x +def split_format(fmt): + i = 0 + while i < len(fmt): + if fmt[i] == 'Z': + n = 2 + else: + n = 1 + yield fmt[i:i + n] + i += n + def gen_item(fmt, obj): """Return single random item.""" mode, chars = fmt.split('#') x = [] - for c in chars: + for c in split_format(chars): x.append(randrange_fmt(mode, c, obj)) return x[0] if len(x) == 1 else tuple(x) @@ -254,9 +266,7 @@ def is_byte_format(fmt): def is_memoryview_format(fmt): """format suitable for memoryview""" - x = len(fmt) - return ((x == 1 or (x == 2 and fmt[0] == '@')) and - fmt[x-1] in MEMORYVIEW) + return fmt.removeprefix('@') in MEMORYVIEW NON_BYTE_FORMAT = [c for c in fmtdict['@'] if not is_byte_format(c)] @@ -648,14 +658,22 @@ def ndarray_from_structure(items, fmt, t, flags=0): return ndarray(items, shape=shape, strides=strides, format=fmt, offset=offset, flags=ND_WRITABLE|flags) +# Convert PEP 3118 formats to numpy dtypes +FORMAT_TO_DTYPE = { + 'Zf': 'F', + 'Zd': 'D', +} + def numpy_array_from_structure(items, fmt, t): """Return numpy_array from the tuple returned by rand_structure()""" memlen, itemsize, ndim, shape, strides, offset = t buf = bytearray(memlen) for j, v in enumerate(items): struct.pack_into(fmt, buf, j*itemsize, v) + # Replace Zd/Zf formats with D/F dtypes + dtype = FORMAT_TO_DTYPE.get(fmt, fmt) return numpy_array(buffer=buf, shape=shape, strides=strides, - dtype=fmt, offset=offset) + dtype=dtype, offset=offset) # ====================================================================== @@ -3037,7 +3055,7 @@ def test_memoryview_assign(self): continue m2 = m1.cast(fmt) lo, hi = _range - if fmt in "dfDF": + if fmt in ("d", "f", "D", "F", "Zd", "Zf"): lo, hi = -2**1024, 2**1024 if fmt != 'P': # PyLong_AsVoidPtr() accepts negative numbers self.assertRaises(ValueError, m2.__setitem__, 0, lo-1) diff --git a/Lib/test/test_ctypes/test_c_simple_type_meta.py b/Lib/test/test_ctypes/test_c_simple_type_meta.py index fd261acf497..3b4fcf9c42a 100644 --- a/Lib/test/test_ctypes/test_c_simple_type_meta.py +++ b/Lib/test/test_ctypes/test_c_simple_type_meta.py @@ -213,14 +213,12 @@ def test_bad_type_message(self): class F(metaclass=PyCSimpleType): _type_ = "\0" message = str(cm.exception) - expected_type_chars = list('cbBhHiIlLdDFGfuzZqQPXOv?g') - if not hasattr(ctypes, 'c_float_complex'): - expected_type_chars.remove('F') - expected_type_chars.remove('D') - expected_type_chars.remove('G') + expected_type_chars = list('cbBhHiIlLdfuzZqQPXOv?g') if not MS_WINDOWS: expected_type_chars.remove('X') self.assertIn("'" + ''.join(expected_type_chars) + "'", message) + if hasattr(ctypes, 'c_float_complex'): + self.assertIn("'Zf', 'Zd', 'Zg'", message) def test_creating_pointer_in_dunder_init_3(self): """Check if interfcase subclasses properly creates according internal diff --git a/Lib/test/test_ctypes/test_numbers.py b/Lib/test/test_ctypes/test_numbers.py index c57c58eb002..b7df08f6079 100644 --- a/Lib/test/test_ctypes/test_numbers.py +++ b/Lib/test/test_ctypes/test_numbers.py @@ -119,8 +119,11 @@ def test_floats(self): @unittest.skipUnless(hasattr(ctypes, "c_double_complex"), "requires C11 complex type") def test_complex(self): - for t in [ctypes.c_double_complex, ctypes.c_float_complex, - ctypes.c_longdouble_complex]: + for format, t in [ + ('Zd', ctypes.c_double_complex), + ('Zf', ctypes.c_float_complex), + ('Zg', ctypes.c_longdouble_complex), + ]: self.assertEqual(t(1).value, 1+0j) self.assertEqual(t(1.0).value, 1+0j) self.assertEqual(t(1+0.125j).value, 1+0.125j) @@ -128,6 +131,12 @@ def test_complex(self): self.assertEqual(t(FloatLike()).value, 2+0j) self.assertEqual(t(ComplexLike()).value, 1+1j) + prefix = '>' if sys.byteorder == 'big' else '<' + num = t(1.0) + self.assertEqual(memoryview(num).format, prefix + format) + array = (t * 3)() + self.assertEqual(memoryview(array).format, prefix + format) + @unittest.skipUnless(hasattr(ctypes, "c_double_complex"), "requires C11 complex type") def test_complex_round_trip(self): diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index 820574584b5..f777ba583c5 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -637,7 +637,7 @@ def check_equal(view, is_equal): check_equal(m, True) # Test complex formats - for complex_format in 'FD': + for complex_format in ('F', 'D', 'Zf', 'Zd'): with self.subTest(format=complex_format): data = struct.pack(complex_format * 3, 1.0, 2.0, float('nan')) m = memoryview(data).cast(complex_format) @@ -723,6 +723,10 @@ def test_complex_types(self): double_complex_view = memoryview(double_complex_data).cast('D') self.assertEqual(float_complex_view.nbytes * 2, double_complex_view.nbytes) self.assertListEqual(float_complex_view.tolist(), double_complex_view.tolist()) + float_complex_view = memoryview(float_complex_data).cast('Zf') + double_complex_view = memoryview(double_complex_data).cast('Zd') + self.assertEqual(float_complex_view.nbytes * 2, double_complex_view.nbytes) + self.assertListEqual(float_complex_view.tolist(), double_complex_view.tolist()) def test_memoryview_hex(self): # Issue #9951: memoryview.hex() segfaults with non-contiguous buffers. diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 6479676f155..edd85df633f 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -995,7 +995,11 @@ def test_c_complex_round_trip(self): values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2, -3, INF, -INF, NAN], 2)] for z in values: - for f in ['F', 'D', '>F', '>D', 'F', '>D', '>Zf', '>Zd', + '' : '<'; - result[1] = pep_code; - result[2] = '\0'; + strcpy(result + 1, pep_code); return result; } @@ -2347,7 +2345,6 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) { PyObject *proto; const char *proto_str; - Py_ssize_t proto_len; PyMethodDef *ml; struct fielddesc *fmt; @@ -2365,7 +2362,7 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) return -1; } if (PyUnicode_Check(proto)) { - proto_str = PyUnicode_AsUTF8AndSize(proto, &proto_len); + proto_str = PyUnicode_AsUTF8(proto); if (!proto_str) goto error; } else { @@ -2373,19 +2370,25 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) "class must define a '_type_' string attribute"); goto error; } - if (proto_len != 1) { - PyErr_SetString(PyExc_ValueError, - "class must define a '_type_' attribute " - "which must be a string of length 1"); - goto error; - } fmt = _ctypes_get_fielddesc(proto_str); if (!fmt) { - PyErr_Format(PyExc_AttributeError, - "class must define a '_type_' attribute which must be\n" - "a single character string containing one of the\n" - "supported types: '%s'.", - _ctypes_get_simple_type_chars()); + const char *complex_formats = _ctypes_get_complex_type_formats(); + if (complex_formats) { + PyErr_Format(PyExc_AttributeError, + "class must define a '_type_' attribute which must be\n" + "a single character string containing one of the\n" + "supported types: '%s', or one of these strings:\n" + "%s.", + _ctypes_get_simple_type_chars(), + complex_formats); + } + else { + PyErr_Format(PyExc_AttributeError, + "class must define a '_type_' attribute which must be\n" + "a single character string containing one of the\n" + "supported types: '%s'.\n", + _ctypes_get_simple_type_chars()); + } goto error; } @@ -2403,9 +2406,9 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) stginfo->setfunc = fmt->setfunc; stginfo->getfunc = fmt->getfunc; #ifdef WORDS_BIGENDIAN - stginfo->format = _ctypes_alloc_format_string_for_type(proto_str[0], 1); + stginfo->format = _ctypes_alloc_format_string_for_type(proto_str, 1); #else - stginfo->format = _ctypes_alloc_format_string_for_type(proto_str[0], 0); + stginfo->format = _ctypes_alloc_format_string_for_type(proto_str, 0); #endif if (stginfo->format == NULL) { Py_DECREF(proto); @@ -2432,9 +2435,15 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) ml = c_char_p_methods; stginfo->flags |= TYPEFLAG_ISPOINTER; break; - case 'Z': /* c_wchar_p */ - ml = c_wchar_p_methods; - stginfo->flags |= TYPEFLAG_ISPOINTER; + case 'Z': + if (proto_str[1] == '\0') { + /* "Z": c_wchar_p */ + ml = c_wchar_p_methods; + stginfo->flags |= TYPEFLAG_ISPOINTER; + } + else { + ml = NULL; + } break; case 'P': /* c_void_p */ ml = c_void_p_methods; diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 9a1c1ff8bb9..e208e27c5db 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -641,9 +641,9 @@ union result { double d; float f; void *p; - double D[2]; - float F[2]; - long double G[2]; + double Zd[2]; + float Zf[2]; + long double Zg[2]; }; struct argument { diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index b0dc11fdddc..55853c35f39 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -767,9 +767,9 @@ d_get(void *ptr, Py_ssize_t size) corresponding real type; the first element is equal to the real part, and the second element to the imaginary part, of the complex number." */ -/* D: double complex */ +/* Zd: double complex */ static PyObject * -D_set(void *ptr, PyObject *value, Py_ssize_t size) +Zd_set(void *ptr, PyObject *value, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(double))); Py_complex c = PyComplex_AsCComplex(value); @@ -783,7 +783,7 @@ D_set(void *ptr, PyObject *value, Py_ssize_t size) } static PyObject * -D_get(void *ptr, Py_ssize_t size) +Zd_get(void *ptr, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(double))); double x[2]; @@ -793,7 +793,7 @@ D_get(void *ptr, Py_ssize_t size) } static PyObject * -D_set_sw(void *ptr, PyObject *value, Py_ssize_t size) +Zd_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(double))); Py_complex c = PyComplex_AsCComplex(value); @@ -818,7 +818,7 @@ D_set_sw(void *ptr, PyObject *value, Py_ssize_t size) } static PyObject * -D_get_sw(void *ptr, Py_ssize_t size) +Zd_get_sw(void *ptr, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(double))); #ifdef WORDS_BIGENDIAN @@ -830,9 +830,9 @@ D_get_sw(void *ptr, Py_ssize_t size) #endif } -/* F: float complex */ +/* Zf: float complex */ static PyObject * -F_set(void *ptr, PyObject *value, Py_ssize_t size) +Zf_set(void *ptr, PyObject *value, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(float))); Py_complex c = PyComplex_AsCComplex(value); @@ -846,7 +846,7 @@ F_set(void *ptr, PyObject *value, Py_ssize_t size) } static PyObject * -F_get(void *ptr, Py_ssize_t size) +Zf_get(void *ptr, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(float))); float x[2]; @@ -856,7 +856,7 @@ F_get(void *ptr, Py_ssize_t size) } static PyObject * -F_set_sw(void *ptr, PyObject *value, Py_ssize_t size) +Zf_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(float))); Py_complex c = PyComplex_AsCComplex(value); @@ -881,7 +881,7 @@ F_set_sw(void *ptr, PyObject *value, Py_ssize_t size) } static PyObject * -F_get_sw(void *ptr, Py_ssize_t size) +Zf_get_sw(void *ptr, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(float))); #ifdef WORDS_BIGENDIAN @@ -893,9 +893,9 @@ F_get_sw(void *ptr, Py_ssize_t size) #endif } -/* G: long double complex */ +/* Zg: long double complex */ static PyObject * -G_set(void *ptr, PyObject *value, Py_ssize_t size) +Zg_set(void *ptr, PyObject *value, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(long double))); Py_complex c = PyComplex_AsCComplex(value); @@ -909,7 +909,7 @@ G_set(void *ptr, PyObject *value, Py_ssize_t size) } static PyObject * -G_get(void *ptr, Py_ssize_t size) +Zg_get(void *ptr, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(long double))); long double x[2]; @@ -917,7 +917,8 @@ G_get(void *ptr, Py_ssize_t size) memcpy(&x, ptr, sizeof(x)); return PyComplex_FromDoubles((double)x[0], (double)x[1]); } -#endif +#endif // _Py_FFI_SUPPORT_C_COMPLEX + /* d: double */ static PyObject * @@ -1451,7 +1452,7 @@ struct formattable { for nbytes in 8, 16, 32, 64: for sgn in 'i', 'u': print(f' struct fielddesc fmt_{sgn}{nbytes};') -for code in 'sbBcdFDGgfhHiIlLqQPzuUZXvO': +for code in 'sbBcdgfhHiIlLqQPzuUZXvO': print(f' struct fielddesc fmt_{code};') [python start generated code]*/ struct fielddesc fmt_i8; @@ -1467,9 +1468,6 @@ for code in 'sbBcdFDGgfhHiIlLqQPzuUZXvO': struct fielddesc fmt_B; struct fielddesc fmt_c; struct fielddesc fmt_d; - struct fielddesc fmt_F; - struct fielddesc fmt_D; - struct fielddesc fmt_G; struct fielddesc fmt_g; struct fielddesc fmt_f; struct fielddesc fmt_h; @@ -1488,7 +1486,10 @@ for code in 'sbBcdFDGgfhHiIlLqQPzuUZXvO': struct fielddesc fmt_X; struct fielddesc fmt_v; struct fielddesc fmt_O; -/*[python end generated code: output=f5a07c066fedaca6 input=ffa5d46c29dfb07a]*/ +/*[python end generated code: output=266ae6d30b6286a1 input=a1b7a263c7cf681f]*/ + struct fielddesc fmt_Zf; + struct fielddesc fmt_Zd; + struct fielddesc fmt_Zg; // bool has code '?': struct fielddesc fmt_bool; @@ -1498,7 +1499,7 @@ for code in 'sbBcdFDGgfhHiIlLqQPzuUZXvO': // Result of _ctypes_get_simple_type_chars. Initialized just after // the rest of formattable, so we stash it here. - char simple_type_chars[26]; + char simple_type_chars[23]; }; static struct formattable formattable; @@ -1617,7 +1618,7 @@ for base_code, base_c_type in [ (base_code.upper(), 'unsigned ' + base_c_type, 'u' + base_c_type), ]: print(f' formattable.fmt_{code} = *FIXINT_FIELDDESC_FOR({c_type});') - print(f" formattable.fmt_{code}.code = '{code}';") + print(f' formattable.fmt_{code}.code = "{code}";') if base_code == 'q': # ffi doesn't have `long long`; keep use the fixint type pass @@ -1625,34 +1626,34 @@ for base_code, base_c_type in [ print(f' formattable.fmt_{code}.pffi_type = &ffi_type_{ffi_type};') [python start generated code]*/ formattable.fmt_b = *FIXINT_FIELDDESC_FOR(signed char); - formattable.fmt_b.code = 'b'; + formattable.fmt_b.code = "b"; formattable.fmt_b.pffi_type = &ffi_type_schar; formattable.fmt_B = *FIXINT_FIELDDESC_FOR(unsigned char); - formattable.fmt_B.code = 'B'; + formattable.fmt_B.code = "B"; formattable.fmt_B.pffi_type = &ffi_type_uchar; formattable.fmt_h = *FIXINT_FIELDDESC_FOR(signed short); - formattable.fmt_h.code = 'h'; + formattable.fmt_h.code = "h"; formattable.fmt_h.pffi_type = &ffi_type_sshort; formattable.fmt_H = *FIXINT_FIELDDESC_FOR(unsigned short); - formattable.fmt_H.code = 'H'; + formattable.fmt_H.code = "H"; formattable.fmt_H.pffi_type = &ffi_type_ushort; formattable.fmt_i = *FIXINT_FIELDDESC_FOR(signed int); - formattable.fmt_i.code = 'i'; + formattable.fmt_i.code = "i"; formattable.fmt_i.pffi_type = &ffi_type_sint; formattable.fmt_I = *FIXINT_FIELDDESC_FOR(unsigned int); - formattable.fmt_I.code = 'I'; + formattable.fmt_I.code = "I"; formattable.fmt_I.pffi_type = &ffi_type_uint; formattable.fmt_l = *FIXINT_FIELDDESC_FOR(signed long); - formattable.fmt_l.code = 'l'; + formattable.fmt_l.code = "l"; formattable.fmt_l.pffi_type = &ffi_type_slong; formattable.fmt_L = *FIXINT_FIELDDESC_FOR(unsigned long); - formattable.fmt_L.code = 'L'; + formattable.fmt_L.code = "L"; formattable.fmt_L.pffi_type = &ffi_type_ulong; formattable.fmt_q = *FIXINT_FIELDDESC_FOR(signed long long); - formattable.fmt_q.code = 'q'; + formattable.fmt_q.code = "q"; formattable.fmt_Q = *FIXINT_FIELDDESC_FOR(unsigned long long); - formattable.fmt_Q.code = 'Q'; -/*[python end generated code: output=873c87a2e6b5075a input=ee814ca263aac18e]*/ + formattable.fmt_Q.code = "Q"; +/*[python end generated code: output=b91080b4b821a6da input=7356e281df4debd3]*/ /* Other types have bespoke setters and getters named `@_set` and `@_get`, @@ -1662,7 +1663,7 @@ for base_code, base_c_type in [ #define _TABLE_ENTRY(SYMBOL, FFI_TYPE, ...) \ formattable.fmt_ ## SYMBOL = \ - (struct fielddesc){(#SYMBOL)[0], (FFI_TYPE), __VA_ARGS__}; \ + (struct fielddesc){(#SYMBOL), (FFI_TYPE), __VA_ARGS__}; \ /////////////////////////////////////////////////////////////////////////// #define TABLE_ENTRY(SYMBOL, FFI_TYPE) \ @@ -1677,11 +1678,11 @@ for base_code, base_c_type in [ TABLE_ENTRY_SW(d, &ffi_type_double); #if defined(_Py_FFI_SUPPORT_C_COMPLEX) if (Py_FFI_COMPLEX_AVAILABLE) { - TABLE_ENTRY(D, &ffi_type_complex_double); - TABLE_ENTRY_SW(D, &ffi_type_complex_double); - TABLE_ENTRY(F, &ffi_type_complex_float); - TABLE_ENTRY_SW(F, &ffi_type_complex_float); - TABLE_ENTRY(G, &ffi_type_complex_longdouble); + TABLE_ENTRY(Zd, &ffi_type_complex_double); + TABLE_ENTRY_SW(Zd, &ffi_type_complex_double); + TABLE_ENTRY(Zf, &ffi_type_complex_float); + TABLE_ENTRY_SW(Zf, &ffi_type_complex_float); + TABLE_ENTRY(Zg, &ffi_type_complex_longdouble); } #endif TABLE_ENTRY(g, &ffi_type_longdouble); @@ -1711,12 +1712,12 @@ for base_code, base_c_type in [ // ctypes.c_bool is unsigned for FFI, even where C bool is signed. formattable.fmt_bool = *_ctypes_fixint_fielddesc(sizeof(bool), false); - formattable.fmt_bool.code = '?'; + formattable.fmt_bool.code = "?"; formattable.fmt_bool.setfunc = bool_set; formattable.fmt_bool.getfunc = bool_get; /*[python input] -all_chars = "cbBhHiIlLdDFGfuzZqQPXOv?g" +all_chars = "cbBhHiIlLdfuzZqQPXOv?g" print(f' assert(sizeof(formattable.simple_type_chars) == {len(all_chars)+1});') print(f' int i = 0;') for char in all_chars: @@ -1725,7 +1726,7 @@ for char in all_chars: + f"formattable.simple_type_chars[i++] = '{char}';") print(f" formattable.simple_type_chars[i] = 0;") [python start generated code]*/ - assert(sizeof(formattable.simple_type_chars) == 26); + assert(sizeof(formattable.simple_type_chars) == 23); int i = 0; if (formattable.fmt_c.code) formattable.simple_type_chars[i++] = 'c'; if (formattable.fmt_b.code) formattable.simple_type_chars[i++] = 'b'; @@ -1737,9 +1738,6 @@ print(f" formattable.simple_type_chars[i] = 0;") if (formattable.fmt_l.code) formattable.simple_type_chars[i++] = 'l'; if (formattable.fmt_L.code) formattable.simple_type_chars[i++] = 'L'; if (formattable.fmt_d.code) formattable.simple_type_chars[i++] = 'd'; - if (formattable.fmt_D.code) formattable.simple_type_chars[i++] = 'D'; - if (formattable.fmt_F.code) formattable.simple_type_chars[i++] = 'F'; - if (formattable.fmt_G.code) formattable.simple_type_chars[i++] = 'G'; if (formattable.fmt_f.code) formattable.simple_type_chars[i++] = 'f'; if (formattable.fmt_u.code) formattable.simple_type_chars[i++] = 'u'; if (formattable.fmt_z.code) formattable.simple_type_chars[i++] = 'z'; @@ -1753,24 +1751,34 @@ print(f" formattable.simple_type_chars[i] = 0;") if (formattable.fmt_bool.code) formattable.simple_type_chars[i++] = '?'; if (formattable.fmt_g.code) formattable.simple_type_chars[i++] = 'g'; formattable.simple_type_chars[i] = 0; -/*[python end generated code: output=2aa52670d1570f18 input=cff3e7cb95adac61]*/ +/*[python end generated code: output=b78c8b7eed73d45a input=30ddc50637dd8ee4]*/ } #undef FIXINT_FIELDDESC_FOR _Py_COMP_DIAG_POP -char * +const char* _ctypes_get_simple_type_chars(void) { return formattable.simple_type_chars; } +const char* +_ctypes_get_complex_type_formats(void) { + if (Py_FFI_COMPLEX_AVAILABLE) { + return "'Zf', 'Zd', 'Zg'"; + } + else { + return NULL; + } +} + struct fielddesc * _ctypes_get_fielddesc(const char *fmt) { struct fielddesc *result = NULL; switch(fmt[0]) { /*[python input] -for code in 'sbBcdDFGgfhHiIlLqQPzuUZXvO': +for code in 'sbBcdgfhHiIlLqQPzuUXvO': print(f" case '{code}': result = &formattable.fmt_{code}; break;") [python start generated code]*/ case 's': result = &formattable.fmt_s; break; @@ -1778,9 +1786,6 @@ for code in 'sbBcdDFGgfhHiIlLqQPzuUZXvO': case 'B': result = &formattable.fmt_B; break; case 'c': result = &formattable.fmt_c; break; case 'd': result = &formattable.fmt_d; break; - case 'D': result = &formattable.fmt_D; break; - case 'F': result = &formattable.fmt_F; break; - case 'G': result = &formattable.fmt_G; break; case 'g': result = &formattable.fmt_g; break; case 'f': result = &formattable.fmt_f; break; case 'h': result = &formattable.fmt_h; break; @@ -1795,11 +1800,19 @@ for code in 'sbBcdDFGgfhHiIlLqQPzuUZXvO': case 'z': result = &formattable.fmt_z; break; case 'u': result = &formattable.fmt_u; break; case 'U': result = &formattable.fmt_U; break; - case 'Z': result = &formattable.fmt_Z; break; case 'X': result = &formattable.fmt_X; break; case 'v': result = &formattable.fmt_v; break; case 'O': result = &formattable.fmt_O; break; -/*[python end generated code: output=6e5c91940732fde9 input=902223feffc2fe38]*/ +/*[python end generated code: output=8e95bd0d49efb1c8 input=82d4ee1538b9b282]*/ + case 'Z': { + switch(fmt[1]) { + case '\0': result = &formattable.fmt_Z; break; + case 'd': result = &formattable.fmt_Zd; break; + case 'f': result = &formattable.fmt_Zf; break; + case 'g': result = &formattable.fmt_Zg; break; + } + break; + } case '?': result = &formattable.fmt_bool; break; } if (!result || !result->code) { diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 21743eb80ee..7b6b7f08582 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -280,7 +280,7 @@ extern CThunkObject *_ctypes_alloc_callback(ctypes_state *st, int flags); /* a table entry describing a predefined ctypes type */ struct fielddesc { - char code; + const char *code; ffi_type *pffi_type; /* always statically allocated */ SETFUNC setfunc; GETFUNC getfunc; @@ -289,7 +289,8 @@ struct fielddesc { }; // Get all single-character type codes (for use in error messages) -extern char *_ctypes_get_simple_type_chars(void); +extern const char* _ctypes_get_simple_type_chars(void); +extern const char* _ctypes_get_complex_type_formats(void); typedef struct CFieldObject { PyObject_HEAD diff --git a/Modules/_struct.c b/Modules/_struct.c index 47119cb3961..3a970d99bb3 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -45,7 +45,7 @@ static struct PyModuleDef _structmodule; /* The translation function for each format character is table driven */ typedef struct _formatdef { - char format; + const char *format; Py_ssize_t size; Py_ssize_t alignment; PyObject* (*unpack)(_structmodulestate *, const char *, @@ -327,13 +327,13 @@ _range_error(_structmodulestate *state, const formatdef *f, int is_unsigned) assert(f->size >= 1 && f->size <= SIZEOF_SIZE_T); if (is_unsigned) PyErr_Format(state->StructError, - "'%c' format requires 0 <= number <= %zu", + "'%s' format requires 0 <= number <= %zu", f->format, ulargest); else { const Py_ssize_t largest = (Py_ssize_t)(ulargest >> 1); PyErr_Format(state->StructError, - "'%c' format requires %zd <= number <= %zd", + "'%s' format requires %zd <= number <= %zd", f->format, ~ largest, largest); @@ -708,7 +708,7 @@ np_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) if (get_longlong(state, v, &x) < 0) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) { PyErr_Format(state->StructError, - "'%c' format requires %lld <= number <= %lld", + "'%s' format requires %lld <= number <= %lld", f->format, LLONG_MIN, LLONG_MAX); @@ -726,7 +726,7 @@ np_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f if (get_ulonglong(state, v, &x) < 0) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) { PyErr_Format(state->StructError, - "'%c' format requires 0 <= number <= %llu", + "'%s' format requires 0 <= number <= %llu", f->format, ULLONG_MAX); } @@ -835,29 +835,31 @@ np_void_p(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) } static const formatdef native_table[] = { - {'x', sizeof(char), 0, NULL}, - {'b', sizeof(char), 0, nu_byte, np_byte}, - {'B', sizeof(char), 0, nu_ubyte, np_ubyte}, - {'c', sizeof(char), 0, nu_char, np_char}, - {'s', sizeof(char), 0, NULL}, - {'p', sizeof(char), 0, NULL}, - {'h', sizeof(short), _Alignof(short), nu_short, np_short}, - {'H', sizeof(short), _Alignof(short), nu_ushort, np_ushort}, - {'i', sizeof(int), _Alignof(int), nu_int, np_int}, - {'I', sizeof(int), _Alignof(int), nu_uint, np_uint}, - {'l', sizeof(long), _Alignof(long), nu_long, np_long}, - {'L', sizeof(long), _Alignof(long), nu_ulong, np_ulong}, - {'n', sizeof(size_t), _Alignof(size_t), nu_ssize_t, np_ssize_t}, - {'N', sizeof(size_t), _Alignof(size_t), nu_size_t, np_size_t}, - {'q', sizeof(long long), _Alignof(long long), nu_longlong, np_longlong}, - {'Q', sizeof(long long), _Alignof(long long), nu_ulonglong,np_ulonglong}, - {'?', sizeof(_Bool), _Alignof(_Bool), nu_bool, np_bool}, - {'e', sizeof(short), _Alignof(short), nu_halffloat, np_halffloat}, - {'f', sizeof(float), _Alignof(float), nu_float, np_float}, - {'d', sizeof(double), _Alignof(double), nu_double, np_double}, - {'F', 2*sizeof(float), _Alignof(float), nu_float_complex, np_float_complex}, - {'D', 2*sizeof(double), _Alignof(double), nu_double_complex, np_double_complex}, - {'P', sizeof(void *), _Alignof(void *), nu_void_p, np_void_p}, + {"x", sizeof(char), 0, NULL}, + {"b", sizeof(char), 0, nu_byte, np_byte}, + {"B", sizeof(char), 0, nu_ubyte, np_ubyte}, + {"c", sizeof(char), 0, nu_char, np_char}, + {"s", sizeof(char), 0, NULL}, + {"p", sizeof(char), 0, NULL}, + {"h", sizeof(short), _Alignof(short), nu_short, np_short}, + {"H", sizeof(short), _Alignof(short), nu_ushort, np_ushort}, + {"i", sizeof(int), _Alignof(int), nu_int, np_int}, + {"I", sizeof(int), _Alignof(int), nu_uint, np_uint}, + {"l", sizeof(long), _Alignof(long), nu_long, np_long}, + {"L", sizeof(long), _Alignof(long), nu_ulong, np_ulong}, + {"n", sizeof(size_t), _Alignof(size_t), nu_ssize_t, np_ssize_t}, + {"N", sizeof(size_t), _Alignof(size_t), nu_size_t, np_size_t}, + {"q", sizeof(long long), _Alignof(long long), nu_longlong, np_longlong}, + {"Q", sizeof(long long), _Alignof(long long), nu_ulonglong,np_ulonglong}, + {"?", sizeof(_Bool), _Alignof(_Bool), nu_bool, np_bool}, + {"e", sizeof(short), _Alignof(short), nu_halffloat, np_halffloat}, + {"f", sizeof(float), _Alignof(float), nu_float, np_float}, + {"d", sizeof(double), _Alignof(double), nu_double, np_double}, + {"F", 2*sizeof(float), _Alignof(float), nu_float_complex, np_float_complex}, + {"D", 2*sizeof(double), _Alignof(double), nu_double_complex, np_double_complex}, + {"Zf", 2*sizeof(float), _Alignof(float), nu_float_complex, np_float_complex}, + {"Zd", 2*sizeof(double), _Alignof(double), nu_double_complex, np_double_complex}, + {"P", sizeof(void *), _Alignof(void *), nu_void_p, np_void_p}, {0} }; @@ -1063,7 +1065,7 @@ bp_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) Py_DECREF(v); if (res < 0) { PyErr_Format(state->StructError, - "'%c' format requires %lld <= number <= %lld", + "'%s' format requires %lld <= number <= %lld", f->format, LLONG_MIN, LLONG_MAX); @@ -1088,7 +1090,7 @@ bp_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f Py_DECREF(v); if (res < 0) { PyErr_Format(state->StructError, - "'%c' format requires 0 <= number <= %llu", + "'%s' format requires 0 <= number <= %llu", f->format, ULLONG_MAX); return -1; @@ -1168,26 +1170,28 @@ bp_bool(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) } static formatdef bigendian_table[] = { - {'x', 1, 0, NULL}, - {'b', 1, 0, nu_byte, np_byte}, - {'B', 1, 0, nu_ubyte, np_ubyte}, - {'c', 1, 0, nu_char, np_char}, - {'s', 1, 0, NULL}, - {'p', 1, 0, NULL}, - {'h', 2, 0, bu_short, bp_int}, - {'H', 2, 0, bu_uint, bp_uint}, - {'i', 4, 0, bu_int, bp_int}, - {'I', 4, 0, bu_uint, bp_uint}, - {'l', 4, 0, bu_int, bp_int}, - {'L', 4, 0, bu_uint, bp_uint}, - {'q', 8, 0, bu_longlong, bp_longlong}, - {'Q', 8, 0, bu_ulonglong, bp_ulonglong}, - {'?', 1, 0, bu_bool, bp_bool}, - {'e', 2, 0, bu_halffloat, bp_halffloat}, - {'f', 4, 0, bu_float, bp_float}, - {'d', 8, 0, bu_double, bp_double}, - {'F', 8, 0, bu_float_complex, bp_float_complex}, - {'D', 16, 0, bu_double_complex, bp_double_complex}, + {"x", 1, 0, NULL}, + {"b", 1, 0, nu_byte, np_byte}, + {"B", 1, 0, nu_ubyte, np_ubyte}, + {"c", 1, 0, nu_char, np_char}, + {"s", 1, 0, NULL}, + {"p", 1, 0, NULL}, + {"h", 2, 0, bu_short, bp_int}, + {"H", 2, 0, bu_uint, bp_uint}, + {"i", 4, 0, bu_int, bp_int}, + {"I", 4, 0, bu_uint, bp_uint}, + {"l", 4, 0, bu_int, bp_int}, + {"L", 4, 0, bu_uint, bp_uint}, + {"q", 8, 0, bu_longlong, bp_longlong}, + {"Q", 8, 0, bu_ulonglong, bp_ulonglong}, + {"?", 1, 0, bu_bool, bp_bool}, + {"e", 2, 0, bu_halffloat, bp_halffloat}, + {"f", 4, 0, bu_float, bp_float}, + {"d", 8, 0, bu_double, bp_double}, + {"F", 8, 0, bu_float_complex, bp_float_complex}, + {"D", 16, 0, bu_double_complex, bp_double_complex}, + {"Zf", 8, 0, bu_float_complex, bp_float_complex}, + {"Zd", 16, 0, bu_double_complex, bp_double_complex}, {0} }; @@ -1387,7 +1391,7 @@ lp_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) Py_DECREF(v); if (res < 0) { PyErr_Format(state->StructError, - "'%c' format requires %lld <= number <= %lld", + "'%s' format requires %lld <= number <= %lld", f->format, LLONG_MIN, LLONG_MAX); @@ -1412,7 +1416,7 @@ lp_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f Py_DECREF(v); if (res < 0) { PyErr_Format(state->StructError, - "'%c' format requires 0 <= number <= %llu", + "'%s' format requires 0 <= number <= %llu", f->format, ULLONG_MAX); return -1; @@ -1482,27 +1486,29 @@ lp_double_complex(_structmodulestate *state, char *p, PyObject *v, const formatd } static formatdef lilendian_table[] = { - {'x', 1, 0, NULL}, - {'b', 1, 0, nu_byte, np_byte}, - {'B', 1, 0, nu_ubyte, np_ubyte}, - {'c', 1, 0, nu_char, np_char}, - {'s', 1, 0, NULL}, - {'p', 1, 0, NULL}, - {'h', 2, 0, lu_short, lp_int}, - {'H', 2, 0, lu_uint, lp_uint}, - {'i', 4, 0, lu_int, lp_int}, - {'I', 4, 0, lu_uint, lp_uint}, - {'l', 4, 0, lu_int, lp_int}, - {'L', 4, 0, lu_uint, lp_uint}, - {'q', 8, 0, lu_longlong, lp_longlong}, - {'Q', 8, 0, lu_ulonglong, lp_ulonglong}, - {'?', 1, 0, bu_bool, bp_bool}, /* Std rep not endian dep, + {"x", 1, 0, NULL}, + {"b", 1, 0, nu_byte, np_byte}, + {"B", 1, 0, nu_ubyte, np_ubyte}, + {"c", 1, 0, nu_char, np_char}, + {"s", 1, 0, NULL}, + {"p", 1, 0, NULL}, + {"h", 2, 0, lu_short, lp_int}, + {"H", 2, 0, lu_uint, lp_uint}, + {"i", 4, 0, lu_int, lp_int}, + {"I", 4, 0, lu_uint, lp_uint}, + {"l", 4, 0, lu_int, lp_int}, + {"L", 4, 0, lu_uint, lp_uint}, + {"q", 8, 0, lu_longlong, lp_longlong}, + {"Q", 8, 0, lu_ulonglong, lp_ulonglong}, + {"?", 1, 0, bu_bool, bp_bool}, /* Std rep not endian dep, but potentially different from native rep -- reuse bx_bool funcs. */ - {'e', 2, 0, lu_halffloat, lp_halffloat}, - {'f', 4, 0, lu_float, lp_float}, - {'d', 8, 0, lu_double, lp_double}, - {'F', 8, 0, lu_float_complex, lp_float_complex}, - {'D', 16, 0, lu_double_complex, lp_double_complex}, + {"e", 2, 0, lu_halffloat, lp_halffloat}, + {"f", 4, 0, lu_float, lp_float}, + {"d", 8, 0, lu_double, lp_double}, + {"F", 8, 0, lu_float_complex, lp_float_complex}, + {"D", 16, 0, lu_double_complex, lp_double_complex}, + {"Zf", 8, 0, lu_float_complex, lp_float_complex}, + {"Zd", 16, 0, lu_double_complex, lp_double_complex}, {0} }; @@ -1523,10 +1529,10 @@ init_endian_tables(void *Py_UNUSED(arg)) entry in the endian table and swap in the native implementations whenever possible (64-bit platforms may not have "standard" sizes) */ - while (native->format != '\0' && other->format != '\0') { + while (native->format != NULL && other->format != NULL) { ptr = other; - while (ptr->format != '\0') { - if (ptr->format == native->format) { + while (ptr->format != NULL) { + if (strcmp(ptr->format, native->format) == 0) { /* Match faster when formats are listed in the same order */ if (ptr == other) @@ -1536,8 +1542,9 @@ init_endian_tables(void *Py_UNUSED(arg)) if (ptr->size != native->size) break; /* Skip _Bool, semantics are different for standard size */ - if (ptr->format == '?') + if (strcmp(ptr->format, "?") == 0) { break; + } ptr->pack = native->pack; ptr->unpack = native->unpack; break; @@ -1576,13 +1583,28 @@ whichtable(const char **pfmt) } +static int +format_equal(const formatdef *e, const char *s) +{ + const char *format = e->format; + size_t i = 0; + while (format[i] == s[i]) { + i++; + if (format[i] == 0) { + return 1; + } + } + return 0; +} + + /* Get the table entry for a format code */ static const formatdef * -getentry(_structmodulestate *state, int c, const formatdef *f) +getentry(_structmodulestate *state, const char *s, const formatdef *f) { - for (; f->format != '\0'; f++) { - if (f->format == c) { + for (; f->format != NULL; f++) { + if (format_equal(f, s)) { return f; } } @@ -1594,11 +1616,11 @@ getentry(_structmodulestate *state, int c, const formatdef *f) /* Align a size according to a format code. Return -1 on overflow. */ static Py_ssize_t -align(Py_ssize_t size, char c, const formatdef *e) +align(Py_ssize_t size, const char *s, const formatdef *e) { Py_ssize_t extra; - if (e->format == c) { + if (format_equal(e, s)) { if (e->alignment && size > 0) { extra = (e->alignment - 1) - (size - 1) % (e->alignment); if (extra > PY_SSIZE_T_MAX - size) @@ -1670,7 +1692,8 @@ prepare_s(PyStructObject *self, PyObject *format) else num = 1; - e = getentry(state, c, f); + s--; + e = getentry(state, s, f); if (e == NULL) return -1; @@ -1696,9 +1719,10 @@ prepare_s(PyStructObject *self, PyObject *format) } itemsize = e->size; - size = align(size, c, e); + size = align(size, s, e); if (size == -1) goto overflow; + s += strlen(e->format); /* if (size + num * itemsize > PY_SSIZE_T_MAX) { ... } */ if (num > (PY_SSIZE_T_MAX - size) / itemsize) @@ -1731,9 +1755,11 @@ prepare_s(PyStructObject *self, PyObject *format) else num = 1; - e = getentry(state, c, f); + s--; + e = getentry(state, s, f); + size = align(size, s, e); + s += strlen(e->format); - size = align(size, c, e); if (c == 's' || c == 'p') { codes->offset = size; codes->size = num; @@ -2024,9 +2050,9 @@ s_unpack_internal(PyStructObject *soself, const char *startfrom, Py_ssize_t j = code->repeat; while (j--) { PyObject *v; - if (e->format == 's') { + if (strcmp(e->format, "s") == 0) { v = PyBytes_FromStringAndSize(res, code->size); - } else if (e->format == 'p') { + } else if (strcmp(e->format, "p") == 0) { Py_ssize_t n; if (code->size == 0) { n = 0; @@ -2319,7 +2345,7 @@ s_pack_internal(PyStructObject *soself, PyObject *const *args, Py_ssize_t j = code->repeat; while (j--) { PyObject *v = args[i++]; - if (e->format == 's') { + if (strcmp(e->format, "s") == 0) { Py_ssize_t n; int isstring; const void *p; @@ -2341,7 +2367,7 @@ s_pack_internal(PyStructObject *soself, PyObject *const *args, n = code->size; if (n > 0) memcpy(res, p, n); - } else if (e->format == 'p') { + } else if (strcmp(e->format, "p") == 0) { Py_ssize_t n; int isstring; const void *p; diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index b01e92eb887..b614053f61b 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -33,12 +33,11 @@ static struct PyModuleDef arraymodule; * functions aren't visible yet. */ struct arraydescr { - char typecode; + const char *typecode; int itemsize; PyObject * (*getitem)(struct arrayobject *, Py_ssize_t); int (*setitem)(struct arrayobject *, Py_ssize_t, PyObject *); int (*compareitems)(const void *, const void *, Py_ssize_t); - const char *formats; int is_integer_type; int is_signed; }; @@ -768,24 +767,26 @@ DEFINE_COMPAREITEMS(QQ, unsigned long long) * typecode. */ static const struct arraydescr descriptors[] = { - {'b', 1, b_getitem, b_setitem, b_compareitems, "b", 1, 1}, - {'B', 1, BB_getitem, BB_setitem, BB_compareitems, "B", 1, 0}, - {'u', sizeof(wchar_t), u_getitem, u_setitem, u_compareitems, "u", 0, 0}, - {'w', sizeof(Py_UCS4), w_getitem, w_setitem, w_compareitems, "w", 0, 0,}, - {'h', sizeof(short), h_getitem, h_setitem, h_compareitems, "h", 1, 1}, - {'H', sizeof(short), HH_getitem, HH_setitem, HH_compareitems, "H", 1, 0}, - {'i', sizeof(int), i_getitem, i_setitem, i_compareitems, "i", 1, 1}, - {'I', sizeof(int), II_getitem, II_setitem, II_compareitems, "I", 1, 0}, - {'l', sizeof(long), l_getitem, l_setitem, l_compareitems, "l", 1, 1}, - {'L', sizeof(long), LL_getitem, LL_setitem, LL_compareitems, "L", 1, 0}, - {'q', sizeof(long long), q_getitem, q_setitem, q_compareitems, "q", 1, 1}, - {'Q', sizeof(long long), QQ_getitem, QQ_setitem, QQ_compareitems, "Q", 1, 0}, - {'e', sizeof(short), e_getitem, e_setitem, NULL, "e", 0, 0}, - {'f', sizeof(float), f_getitem, f_setitem, NULL, "f", 0, 0}, - {'d', sizeof(double), d_getitem, d_setitem, NULL, "d", 0, 0}, - {'F', 2*sizeof(float), cf_getitem, cf_setitem, NULL, "F", 0, 0}, - {'D', 2*sizeof(double), cd_getitem, cd_setitem, NULL, "D", 0, 0}, - {'\0', 0, 0, 0, 0, 0, 0} /* Sentinel */ + {"b", 1, b_getitem, b_setitem, b_compareitems, 1, 1}, + {"B", 1, BB_getitem, BB_setitem, BB_compareitems, 1, 0}, + {"u", sizeof(wchar_t), u_getitem, u_setitem, u_compareitems, 0, 0}, + {"w", sizeof(Py_UCS4), w_getitem, w_setitem, w_compareitems, 0, 0,}, + {"h", sizeof(short), h_getitem, h_setitem, h_compareitems, 1, 1}, + {"H", sizeof(short), HH_getitem, HH_setitem, HH_compareitems, 1, 0}, + {"i", sizeof(int), i_getitem, i_setitem, i_compareitems, 1, 1}, + {"I", sizeof(int), II_getitem, II_setitem, II_compareitems, 1, 0}, + {"l", sizeof(long), l_getitem, l_setitem, l_compareitems, 1, 1}, + {"L", sizeof(long), LL_getitem, LL_setitem, LL_compareitems, 1, 0}, + {"q", sizeof(long long), q_getitem, q_setitem, q_compareitems, 1, 1}, + {"Q", sizeof(long long), QQ_getitem, QQ_setitem, QQ_compareitems, 1, 0}, + {"e", sizeof(short), e_getitem, e_setitem, NULL, 0, 0}, + {"f", sizeof(float), f_getitem, f_setitem, NULL, 0, 0}, + {"d", sizeof(double), d_getitem, d_setitem, NULL, 0, 0}, + {"F", 2*sizeof(float), cf_getitem, cf_setitem, NULL, 0, 0}, + {"D", 2*sizeof(double), cd_getitem, cd_setitem, NULL, 0, 0}, + {"Zf", 2*sizeof(float), cf_getitem, cf_setitem, NULL, 0, 0}, + {"Zd", 2*sizeof(double), cd_getitem, cd_setitem, NULL, 0, 0}, + {NULL, 0, 0, 0, 0, 0, 0} /* Sentinel */ }; /**************************************************************************** @@ -1611,7 +1612,9 @@ array_array_byteswap_impl(arrayobject *self) } break; case 8: - if (self->ob_descr->typecode != 'F') { + if (strcmp(self->ob_descr->typecode, "F") != 0 + && strcmp(self->ob_descr->typecode, "Zf") != 0) + { for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) { char p0 = p[0]; char p1 = p[1]; @@ -1645,7 +1648,8 @@ array_array_byteswap_impl(arrayobject *self) } break; case 16: - assert(self->ob_descr->typecode == 'D'); + assert(strcmp(self->ob_descr->typecode, "D") == 0 + || strcmp(self->ob_descr->typecode, "Zd") == 0); for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) { char t0 = p[0]; char t1 = p[1]; @@ -1985,15 +1989,15 @@ static PyObject * array_array_fromunicode_impl(arrayobject *self, PyObject *ustr) /*[clinic end generated code: output=24359f5e001a7f2b input=158d47c302f27ca1]*/ { - int typecode = self->ob_descr->typecode; - if (typecode != 'u' && typecode != 'w') { + const char *typecode = self->ob_descr->typecode; + if (strcmp(typecode, "u") != 0 && strcmp(typecode, "w") != 0) { PyErr_SetString(PyExc_ValueError, "fromunicode() may only be called on " "unicode type arrays ('u' or 'w')"); return NULL; } - if (typecode == 'u') { + if (strcmp(typecode, "u") == 0) { Py_ssize_t ustr_length = PyUnicode_AsWideChar(ustr, NULL, 0); assert(ustr_length > 0); if (ustr_length > 1) { @@ -2008,7 +2012,7 @@ array_array_fromunicode_impl(arrayobject *self, PyObject *ustr) ustr, ((wchar_t *)self->ob_item) + old_size, ustr_length); } } - else { // typecode == 'w' + else { // typecode == "w" Py_ssize_t ustr_length = PyUnicode_GetLength(ustr); Py_ssize_t old_size = Py_SIZE(self); Py_ssize_t new_size = old_size + ustr_length; @@ -2045,16 +2049,16 @@ static PyObject * array_array_tounicode_impl(arrayobject *self) /*[clinic end generated code: output=08e442378336e1ef input=6690997213d219db]*/ { - int typecode = self->ob_descr->typecode; - if (typecode != 'u' && typecode != 'w') { + const char *typecode = self->ob_descr->typecode; + if (strcmp(typecode, "u") != 0 && strcmp(typecode, "w") != 0) { PyErr_SetString(PyExc_ValueError, "tounicode() may only be called on unicode type arrays ('u' or 'w')"); return NULL; } - if (typecode == 'u') { + if (strcmp(typecode, "u") == 0) { return PyUnicode_FromWideChar((wchar_t *) self->ob_item, Py_SIZE(self)); } - else { // typecode == 'w' + else { // typecode == "w" int byteorder = 0; // native byteorder return PyUnicode_DecodeUTF32((const char *) self->ob_item, Py_SIZE(self) * 4, NULL, &byteorder); @@ -2121,14 +2125,14 @@ static const struct mformatdescr { * be found. */ static enum machine_format_code -typecode_to_mformat_code(char typecode) +typecode_to_mformat_code(const char *typecode) { const int is_big_endian = PY_BIG_ENDIAN; size_t intsize; int is_signed; - switch (typecode) { + switch (typecode[0]) { case 'b': return SIGNED_INT8; case 'B': @@ -2163,6 +2167,21 @@ typecode_to_mformat_code(char typecode) return _PY_FLOAT_BIG_ENDIAN ? \ IEEE_754_DOUBLE_COMPLEX_BE : IEEE_754_DOUBLE_COMPLEX_LE; + case 'Z': { + switch (typecode[1]) { + case 'f': + return _PY_FLOAT_BIG_ENDIAN ? \ + IEEE_754_FLOAT_COMPLEX_BE : IEEE_754_FLOAT_COMPLEX_LE; + + case 'd': + return _PY_FLOAT_BIG_ENDIAN ? \ + IEEE_754_DOUBLE_COMPLEX_BE : IEEE_754_DOUBLE_COMPLEX_LE; + + default: + return UNKNOWN_FORMAT; + } + } + /* Integers */ case 'h': intsize = sizeof(short); @@ -2218,7 +2237,7 @@ static PyObject *array_new(PyTypeObject *type, PyObject *args, PyObject *kwds); * Internal: This function wraps the array constructor--i.e., array_new()--to * allow the creation of array objects from C code without having to deal * directly the tuple argument of array_new(). The typecode argument is a - * Unicode character value, like 'i' or 'f' for example, representing an array + * string, like "i" or "f" for example, representing an array * type code. The items argument is a bytes or a list object from which * contains the initial value of the array. * @@ -2226,7 +2245,7 @@ static PyObject *array_new(PyTypeObject *type, PyObject *args, PyObject *kwds); * NULL is returned to indicate a failure. */ static PyObject * -make_array(PyTypeObject *arraytype, char typecode, PyObject *items) +make_array(PyTypeObject *arraytype, const char *typecode, PyObject *items) { PyObject *new_args; PyObject *array_obj; @@ -2235,7 +2254,7 @@ make_array(PyTypeObject *arraytype, char typecode, PyObject *items) assert(arraytype != NULL); assert(items != NULL); - typecode_obj = PyUnicode_FromOrdinal(typecode); + typecode_obj = PyUnicode_FromString(typecode); if (typecode_obj == NULL) return NULL; @@ -2260,7 +2279,7 @@ make_array(PyTypeObject *arraytype, char typecode, PyObject *items) array._array_reconstructor arraytype: object(type="PyTypeObject *") - typecode: int(accept={str}) + typecode: str mformat_code: int(type="enum machine_format_code") items: object / @@ -2270,10 +2289,10 @@ Internal. Used for pickling support. static PyObject * array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, - int typecode, + const char *typecode, enum machine_format_code mformat_code, PyObject *items) -/*[clinic end generated code: output=e05263141ba28365 input=2464dc8f4c7736b5]*/ +/*[clinic end generated code: output=723e3813e0a18b7b input=9f1c331baae742a6]*/ { array_state *state = get_array_state(module); PyObject *converted_items; @@ -2292,11 +2311,12 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, arraytype->tp_name, state->ArrayType->tp_name); return NULL; } - for (descr = descriptors; descr->typecode != '\0'; descr++) { - if ((int)descr->typecode == typecode) + for (descr = descriptors; descr->typecode != NULL; descr++) { + if (strcmp(descr->typecode, typecode) == 0) { break; + } } - if (descr->typecode == '\0') { + if (descr->typecode == NULL) { PyErr_SetString(PyExc_ValueError, "second argument must be a valid type code"); return NULL; @@ -2315,9 +2335,9 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, } /* Fast path: No decoding has to be done. */ - if (mformat_code == typecode_to_mformat_code((char)typecode) || + if (mformat_code == typecode_to_mformat_code(typecode) || mformat_code == UNKNOWN_FORMAT) { - return make_array(arraytype, (char)typecode, items); + return make_array(arraytype, typecode, items); } /* Slow path: Decode the byte string according to the given machine @@ -2493,11 +2513,13 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, * * XXX: Is it possible to write a unit test for this? */ - for (descr = descriptors; descr->typecode != '\0'; descr++) { + for (descr = descriptors; descr->typecode != NULL; descr++) { if (descr->is_integer_type && (size_t)descr->itemsize == mf_descr.size && descr->is_signed == mf_descr.is_signed) + { typecode = descr->typecode; + } } converted_items = PyList_New(itemcount); @@ -2528,7 +2550,7 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, return NULL; } - result = make_array(arraytype, (char)typecode, converted_items); + result = make_array(arraytype, typecode, converted_items); Py_DECREF(converted_items); return result; } @@ -2551,7 +2573,7 @@ array_array___reduce_ex___impl(arrayobject *self, PyTypeObject *cls, PyObject *dict; PyObject *result; PyObject *array_str; - int typecode = self->ob_descr->typecode; + const char *typecode = self->ob_descr->typecode; int mformat_code; long protocol; @@ -2602,7 +2624,7 @@ array_array___reduce_ex___impl(arrayobject *self, PyTypeObject *cls, return NULL; } result = Py_BuildValue( - "O(CO)O", Py_TYPE(self), typecode, list, dict); + "O(sO)O", Py_TYPE(self), typecode, list, dict); Py_DECREF(list); Py_DECREF(dict); return result; @@ -2616,7 +2638,7 @@ array_array___reduce_ex___impl(arrayobject *self, PyTypeObject *cls, assert(state->array_reconstructor != NULL); result = Py_BuildValue( - "O(OCiN)O", state->array_reconstructor, Py_TYPE(self), typecode, + "O(OsiN)O", state->array_reconstructor, Py_TYPE(self), typecode, mformat_code, array_str, dict); Py_DECREF(dict); return result; @@ -2626,8 +2648,8 @@ static PyObject * array_get_typecode(PyObject *op, void *Py_UNUSED(closure)) { arrayobject *a = arrayobject_CAST(op); - char typecode = a->ob_descr->typecode; - return PyUnicode_FromOrdinal(typecode); + const char *typecode = a->ob_descr->typecode; + return PyUnicode_FromString(typecode); } static PyObject * @@ -2676,7 +2698,7 @@ static PyMethodDef array_methods[] = { static PyObject * array_repr(PyObject *op) { - char typecode; + const char *typecode; PyObject *s, *v = NULL; Py_ssize_t len; arrayobject *a = arrayobject_CAST(op); @@ -2684,10 +2706,10 @@ array_repr(PyObject *op) len = Py_SIZE(a); typecode = a->ob_descr->typecode; if (len == 0) { - return PyUnicode_FromFormat("%s('%c')", - _PyType_Name(Py_TYPE(a)), (int)typecode); + return PyUnicode_FromFormat("%s('%s')", + _PyType_Name(Py_TYPE(a)), typecode); } - if (typecode == 'u' || typecode == 'w') { + if (strcmp(typecode, "u") == 0 || strcmp(typecode, "w") == 0) { v = array_array_tounicode_impl(a); } else { v = array_array_tolist_impl(a); @@ -2695,8 +2717,8 @@ array_repr(PyObject *op) if (v == NULL) return NULL; - s = PyUnicode_FromFormat("%s('%c', %R)", - _PyType_Name(Py_TYPE(a)), (int)typecode, v); + s = PyUnicode_FromFormat("%s('%s', %R)", + _PyType_Name(Py_TYPE(a)), typecode, v); Py_DECREF(v); return s; } @@ -2956,8 +2978,8 @@ array_buffer_getbuf(PyObject *op, Py_buffer *view, int flags) view->format = NULL; view->internal = NULL; if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { - view->format = (char *)self->ob_descr->formats; - if (sizeof(wchar_t) >= 4 && self->ob_descr->typecode == 'u') { + view->format = (char *)self->ob_descr->typecode; + if (sizeof(wchar_t) >= 4 && strcmp(self->ob_descr->typecode, "u") == 0) { view->format = "w"; } } @@ -2977,7 +2999,7 @@ static PyObject * array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { array_state *state = find_array_state_by_type(type); - int c; + const char *s; PyObject *initial = NULL, *it = NULL; const struct arraydescr *descr; @@ -2986,15 +3008,15 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) !_PyArg_NoKeywords("array.array", kwds)) return NULL; - if (!PyArg_ParseTuple(args, "C|O:array", &c, &initial)) + if (!PyArg_ParseTuple(args, "s|O:array", &s, &initial)) return NULL; - if (PySys_Audit("array.__new__", "CO", - c, initial ? initial : Py_None) < 0) { + if (PySys_Audit("array.__new__", "sO", + s, initial ? initial : Py_None) < 0) { return NULL; } - if (c == 'u') { + if (strcmp(s, "u") == 0) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "The 'u' type code is deprecated and " "will be removed in Python 3.16", @@ -3003,19 +3025,19 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } } - bool is_unicode = c == 'u' || c == 'w'; + bool is_unicode = (strcmp(s, "u") == 0 || strcmp(s, "w") == 0); if (initial && !is_unicode) { if (PyUnicode_Check(initial)) { PyErr_Format(PyExc_TypeError, "cannot use a str to initialize " - "an array with typecode '%c'", c); + "an array with typecode '%s'", s); return NULL; } else if (array_Check(initial, state)) { - int ic = ((arrayobject*)initial)->ob_descr->typecode; - if (ic == 'u' || ic == 'w') { + const char *is = ((arrayobject*)initial)->ob_descr->typecode; + if (strcmp(is, "u") == 0 || strcmp(is, "w") == 0) { PyErr_Format(PyExc_TypeError, "cannot use a unicode array to " - "initialize an array with typecode '%c'", c); + "initialize an array with typecode '%s'", s); return NULL; } } @@ -3027,7 +3049,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) || PyTuple_Check(initial) || (is_unicode && PyUnicode_Check(initial)) || (array_Check(initial, state) - && c == ((arrayobject*)initial)->ob_descr->typecode))) { + && strcmp(s, ((arrayobject*)initial)->ob_descr->typecode) == 0))) { it = PyObject_GetIter(initial); if (it == NULL) return NULL; @@ -3038,8 +3060,8 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) */ initial = NULL; } - for (descr = descriptors; descr->typecode != '\0'; descr++) { - if (descr->typecode == c) { + for (descr = descriptors; descr->typecode != NULL; descr++) { + if (strcmp(descr->typecode, s) == 0) { PyObject *a; Py_ssize_t len; @@ -3089,7 +3111,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_DECREF(v); } else if (initial != NULL && PyUnicode_Check(initial)) { - if (c == 'u') { + if (strcmp(s, "u") == 0) { Py_ssize_t n; wchar_t *ustr = PyUnicode_AsWideCharString(initial, &n); if (ustr == NULL) { @@ -3110,7 +3132,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyMem_Free(ustr); } } - else { // c == 'w' + else { // s == "w" Py_ssize_t n = PyUnicode_GET_LENGTH(initial); Py_UCS4 *ustr = PyUnicode_AsUCS4Copy(initial); if (ustr == NULL) { @@ -3145,7 +3167,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } Py_XDECREF(it); PyErr_SetString(PyExc_ValueError, - "bad typecode (must be b, B, u, w, h, H, i, I, l, L, q, Q, f or d)"); + "bad typecode (must be b, B, u, w, h, H, i, I, l, L, q, Q, f, d, F, D, Zd or Zf)"); return NULL; } @@ -3165,7 +3187,7 @@ string or iterable over elements of the appropriate type.\n\ \n\ Arrays represent basic values and behave very much like lists, except\n\ the type of objects stored in them is constrained. The type is specified\n\ -at object creation time by using a type code, which is a single character.\n\ +at object creation time by using a type code, which is a string.\n\ The following type codes are defined:\n\ \n\ Type code C Type Minimum size in bytes\n\ @@ -3185,6 +3207,8 @@ The following type codes are defined:\n\ 'd' floating-point 8\n\ 'F' float complex 8\n\ 'D' double complex 16\n\ + 'Zf' float complex 8\n\ + 'Zd' double complex 16\n\ \n\ NOTE: The 'u' typecode corresponds to Python's unicode character. On\n\ narrow builds this is 2-bytes on wide builds this is 4-bytes.\n\ @@ -3480,7 +3504,6 @@ static int array_modexec(PyObject *m) { array_state *state = get_array_state(m); - char buffer[Py_ARRAY_LENGTH(descriptors)], *p; PyObject *typecodes; const struct arraydescr *descr; @@ -3519,12 +3542,29 @@ array_modexec(PyObject *m) return -1; } - p = buffer; - for (descr = descriptors; descr->typecode != '\0'; descr++) { - *p++ = (char)descr->typecode; + typecodes = PyList_New(0); + if (typecodes == NULL) { + return -1; } - typecodes = PyUnicode_DecodeASCII(buffer, p - buffer, NULL); - if (PyModule_Add(m, "typecodes", typecodes) < 0) { + for (descr = descriptors; descr->typecode != NULL; descr++) { + PyObject *typecode = PyUnicode_DecodeASCII(descr->typecode, strlen(descr->typecode), NULL); + if (typecode == NULL) { + Py_DECREF(typecodes); + return -1; + } + int res = PyList_Append(typecodes, typecode); + Py_DECREF(typecode); + if (res < 0) { + Py_DECREF(typecodes); + return -1; + } + } + PyObject *tuple = PyList_AsTuple(typecodes); + Py_DECREF(typecodes); + if (tuple == NULL) { + return -1; + } + if (PyModule_Add(m, "typecodes", tuple) < 0) { return -1; } diff --git a/Modules/clinic/arraymodule.c.h b/Modules/clinic/arraymodule.c.h index 8a3fb4b515e..eec47ab2b1f 100644 --- a/Modules/clinic/arraymodule.c.h +++ b/Modules/clinic/arraymodule.c.h @@ -651,7 +651,7 @@ PyDoc_STRVAR(array__array_reconstructor__doc__, static PyObject * array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, - int typecode, + const char *typecode, enum machine_format_code mformat_code, PyObject *items); @@ -660,7 +660,7 @@ array__array_reconstructor(PyObject *module, PyObject *const *args, Py_ssize_t n { PyObject *return_value = NULL; PyTypeObject *arraytype; - int typecode; + const char *typecode; enum machine_format_code mformat_code; PyObject *items; @@ -669,17 +669,18 @@ array__array_reconstructor(PyObject *module, PyObject *const *args, Py_ssize_t n } arraytype = (PyTypeObject *)args[0]; if (!PyUnicode_Check(args[1])) { - _PyArg_BadArgument("_array_reconstructor", "argument 2", "a unicode character", args[1]); + _PyArg_BadArgument("_array_reconstructor", "argument 2", "str", args[1]); goto exit; } - if (PyUnicode_GET_LENGTH(args[1]) != 1) { - PyErr_Format(PyExc_TypeError, - "_array_reconstructor(): argument 2 must be a unicode character, " - "not a string of length %zd", - PyUnicode_GET_LENGTH(args[1])); + Py_ssize_t typecode_length; + typecode = PyUnicode_AsUTF8AndSize(args[1], &typecode_length); + if (typecode == NULL) { + goto exit; + } + if (strlen(typecode) != (size_t)typecode_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); goto exit; } - typecode = PyUnicode_READ_CHAR(args[1], 0); mformat_code = PyLong_AsInt(args[2]); if (mformat_code == -1 && PyErr_Occurred()) { goto exit; @@ -779,4 +780,4 @@ array_arrayiterator___setstate__(PyObject *self, PyObject *state) return return_value; } -/*[clinic end generated code: output=9dcb2fc40710f83d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8699475b51151247 input=a9049054013a1b77]*/ diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index d0f414f7c42..9eba8693c2f 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -1197,10 +1197,11 @@ memory_exit(PyObject *self, PyObject *args) /* Casting format and shape */ /****************************************************************************/ -#define IS_BYTE_FORMAT(f) (f == 'b' || f == 'B' || f == 'c') +#define IS_BYTE_FORMAT(f) \ + (strcmp(f, "b") == 0 || strcmp(f, "B") == 0 || strcmp(f, "c") == 0) static inline Py_ssize_t -get_native_fmtchar(char *result, const char *fmt) +get_native_fmtchar(const char **result, const char *fmt) { Py_ssize_t size = -1; @@ -1220,10 +1221,21 @@ get_native_fmtchar(char *result, const char *fmt) case 'D': size = 2*sizeof(double); break; case '?': size = sizeof(_Bool); break; case 'P': size = sizeof(void *); break; + case 'Z': { + switch (fmt[1]) { + case 'f': size = 2*sizeof(float); break; + case 'd': size = 2*sizeof(double); break; + } + if (size > 0 && fmt[2] == '\0') { + *result = fmt; + return size; + } + break; + } } if (size > 0 && fmt[1] == '\0') { - *result = fmt[0]; + *result = fmt; return size; } @@ -1239,9 +1251,19 @@ get_native_fmtstr(const char *fmt) at = 1; fmt++; } - if (fmt[0] == '\0' || fmt[1] != '\0') { + if (fmt[0] == '\0') { return NULL; } + if (fmt[0] == 'Z') { + if (fmt[2] != '\0') { + return NULL; + } + } + else { + if (fmt[1] != '\0') { + return NULL; + } + } #define RETURN(s) do { return at ? "@" s : s; } while (0) @@ -1264,6 +1286,13 @@ get_native_fmtstr(const char *fmt) case 'e': RETURN("e"); case 'F': RETURN("F"); case 'D': RETURN("D"); + case 'Z': { + switch (fmt[1]) { + case 'f': RETURN("Zf"); + case 'd': RETURN("Zd"); + } + break; + } case '?': RETURN("?"); case 'P': RETURN("P"); } @@ -1281,7 +1310,7 @@ cast_to_1D(PyMemoryViewObject *mv, PyObject *format) { Py_buffer *view = &mv->view; PyObject *asciifmt; - char srcchar, destchar; + const char *srcfmt, *destfmt; Py_ssize_t itemsize; int ret = -1; @@ -1295,7 +1324,7 @@ cast_to_1D(PyMemoryViewObject *mv, PyObject *format) if (asciifmt == NULL) return ret; - itemsize = get_native_fmtchar(&destchar, PyBytes_AS_STRING(asciifmt)); + itemsize = get_native_fmtchar(&destfmt, PyBytes_AS_STRING(asciifmt)); if (itemsize < 0) { PyErr_SetString(PyExc_ValueError, "memoryview: destination format must be a native single " @@ -1303,8 +1332,8 @@ cast_to_1D(PyMemoryViewObject *mv, PyObject *format) goto out; } - if ((get_native_fmtchar(&srcchar, view->format) < 0 || - !IS_BYTE_FORMAT(srcchar)) && !IS_BYTE_FORMAT(destchar)) { + if ((get_native_fmtchar(&srcfmt, view->format) < 0 || + !IS_BYTE_FORMAT(srcfmt)) && !IS_BYTE_FORMAT(destfmt)) { PyErr_SetString(PyExc_TypeError, "memoryview: cannot cast between two non-byte formats"); goto out; @@ -1846,6 +1875,23 @@ unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt) d[1] = PyFloat_Unpack8(ptr + sizeof(double), endian); goto convert_double_complex; + case 'Z': { + switch (fmt[1]) { + case 'f': + d[0] = PyFloat_Unpack4(ptr, endian); + d[1] = PyFloat_Unpack4(ptr + sizeof(float), endian); + goto convert_double_complex; + + case 'd': + d[0] = PyFloat_Unpack8(ptr, endian); + d[1] = PyFloat_Unpack8(ptr + sizeof(double), endian); + goto convert_double_complex; + + default: goto err_format; + } + break; + } + /* bytes object */ case 'c': goto convert_bytes; @@ -2027,6 +2073,31 @@ pack_single(PyMemoryViewObject *self, char *ptr, PyObject *item, const char *fmt } break; + case 'Z': { + switch (fmt[1]) { + case 'f': case 'd': + c = PyComplex_AsCComplex(item); + if (c.real == -1.0 && PyErr_Occurred()) { + goto err_occurred; + } + CHECK_RELEASED_INT_AGAIN(self); + if (fmt[1] == 'd') { + double x[2] = {c.real, c.imag}; + + memcpy(ptr, &x, sizeof(x)); + } + else { + float x[2] = {(float)c.real, (float)c.imag}; + + memcpy(ptr, &x, sizeof(x)); + } + break; + + default: goto err_format; + } + break; + } + /* bool */ case '?': ld = PyObject_IsTrue(item); @@ -2200,6 +2271,8 @@ adjust_fmt(const Py_buffer *view) const char *fmt; fmt = (view->format[0] == '@') ? view->format+1 : view->format; + if (fmt[0] == 'Z' && fmt[1] && fmt[2] == '\0') + return fmt; if (fmt[0] && fmt[1] == '\0') return fmt; @@ -3018,12 +3091,12 @@ struct_unpack_cmp(const char *p, const char *q, } while (0) static inline int -unpack_cmp(const char *p, const char *q, char fmt, +unpack_cmp(const char *p, const char *q, const char *fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) { int equal; - switch (fmt) { + switch (fmt[0]) { /* signed integers and fast path for 'B' */ case 'B': return *((const unsigned char *)p) == *((const unsigned char *)q); @@ -3081,6 +3154,27 @@ unpack_cmp(const char *p, const char *q, char fmt, memcpy(&y, q, sizeof(y)); return (x[0] == y[0]) && (x[1] == y[1]); } + case 'Z': { + switch (fmt[1]) { + case 'f': + { + float x[2], y[2]; + + memcpy(&x, p, sizeof(x)); + memcpy(&y, q, sizeof(y)); + return (x[0] == y[0]) && (x[1] == y[1]); + } + case 'd': + { + double x[2], y[2]; + + memcpy(&x, p, sizeof(x)); + memcpy(&y, q, sizeof(y)); + return (x[0] == y[0]) && (x[1] == y[1]); + } + } + break; + } /* bytes object */ case 'c': return *p == *q; @@ -3106,7 +3200,7 @@ static int cmp_base(const char *p, const char *q, const Py_ssize_t *shape, const Py_ssize_t *pstrides, const Py_ssize_t *psuboffsets, const Py_ssize_t *qstrides, const Py_ssize_t *qsuboffsets, - char fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) + const char *fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) { Py_ssize_t i; int equal; @@ -3129,7 +3223,7 @@ cmp_rec(const char *p, const char *q, Py_ssize_t ndim, const Py_ssize_t *shape, const Py_ssize_t *pstrides, const Py_ssize_t *psuboffsets, const Py_ssize_t *qstrides, const Py_ssize_t *qsuboffsets, - char fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) + const char *fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) { Py_ssize_t i; int equal; @@ -3168,7 +3262,7 @@ memory_richcompare(PyObject *v, PyObject *w, int op) Py_buffer *ww = NULL; struct unpacker *unpack_v = NULL; struct unpacker *unpack_w = NULL; - char vfmt, wfmt; + const char *vfmt, *wfmt; int equal = MV_COMPARE_NOT_IMPL; if (op != Py_EQ && op != Py_NE) @@ -3228,15 +3322,15 @@ memory_richcompare(PyObject *v, PyObject *w, int op) /* Use fast unpacking for identical primitive C type formats. */ if (get_native_fmtchar(&vfmt, vv->format) < 0) - vfmt = '_'; + vfmt = "_"; if (get_native_fmtchar(&wfmt, ww->format) < 0) - wfmt = '_'; - if (vfmt == '_' || wfmt == '_' || vfmt != wfmt) { + wfmt = "_"; + if (strcmp(vfmt, "_") == 0 || strcmp(wfmt, "_") == 0 || strcmp(vfmt, wfmt) != 0) { /* Use struct module unpacking. NOTE: Even for equal format strings, memcmp() cannot be used for item comparison since it would give incorrect results in the case of NaNs or uninitialized padding bytes. */ - vfmt = '_'; + vfmt = "_"; unpack_v = struct_get_unpacker(vv->format, vv->itemsize); if (unpack_v == NULL) { equal = fix_struct_error_int(); @@ -3299,7 +3393,7 @@ memory_hash(PyObject *_self) Py_buffer *view = &self->view; char *mem = view->buf; Py_ssize_t ret; - char fmt; + const char *fmt; CHECK_RELEASED_INT(self);