diff --git a/Doc/library/array.rst b/Doc/library/array.rst index 4468edb6efa654..0bca9239bb52ba 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 diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst index fa0fb19d852f86..a828ad4e49f880 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, @@ -368,7 +375,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 7ea7c901eceb19..03e9e544ff9302 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -679,7 +679,7 @@ array ----- * Support the :c:expr:`float complex` and :c:expr:`double complex` C types: - formatting characters ``'F'`` and ``'D'`` respectively. + formatting characters ``'F'``/``'Zd'`` and ``'D'``/``'Zd'`` respectively. (Contributed by Sergey B Kirpichev in :gh:`146151`.) * Support half-floats (16-bit IEEE 754 binary interchange format): formatting diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 4493349798d9c8..4ee1fea6a9297c 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -42,7 +42,7 @@ 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') @support.cpython_only 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 fd261acf49741f..c559b7c649e679 100644 --- a/Lib/test/test_ctypes/test_c_simple_type_meta.py +++ b/Lib/test/test_ctypes/test_c_simple_type_meta.py @@ -221,6 +221,8 @@ class F(metaclass=PyCSimpleType): 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 c57c58eb002328..2718418a5c436c 100644 --- a/Lib/test/test_ctypes/test_numbers.py +++ b/Lib/test/test_ctypes/test_numbers.py @@ -119,8 +119,20 @@ def test_floats(self): @unittest.skipUnless(hasattr(ctypes, "c_double_complex"), "requires C11 complex type") def test_complex(self): + class c_double_complex2(ctypes._SimpleCData): + _type_ = "Zd" + ctypes._check_size(c_double_complex2) + + class c_float_complex2(ctypes._SimpleCData): + _type_ = "Zf" + ctypes._check_size(c_float_complex2) + + class c_longdouble_complex2(ctypes._SimpleCData): + _type_ = "Zg" + for t in [ctypes.c_double_complex, ctypes.c_float_complex, - ctypes.c_longdouble_complex]: + ctypes.c_longdouble_complex, + c_double_complex2, c_float_complex2, c_longdouble_complex2]: 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) diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index 820574584b56b2..f777ba583c5c06 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 6479676f155eca..edd85df633fc3b 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', + 'code) { diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 21743eb80ee976..7b6b7f08582251 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 c235e27a415543..f1a6ecada9782f 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 a86a7561271b87..dadf7fd82b8023 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,7 @@ array_array_byteswap_impl(arrayobject *self) } break; case 8: - if (self->ob_descr->typecode != 'F') { + if (strcmp(self->ob_descr->typecode, "F") != 0) { for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) { char p0 = p[0]; char p1 = p[1]; @@ -1645,7 +1646,7 @@ array_array_byteswap_impl(arrayobject *self) } break; case 16: - assert(self->ob_descr->typecode == 'D'); + assert(strcmp(self->ob_descr->typecode, "D") == 0); for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) { char t0 = p[0]; char t1 = p[1]; @@ -1985,15 +1986,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 +2009,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 +2046,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 +2122,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 +2164,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 +2234,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 +2242,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 +2251,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 +2276,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 +2286,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 +2308,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 +2332,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 +2510,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 +2547,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 +2570,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 +2621,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 +2635,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 +2645,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 +2695,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 +2703,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 +2714,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 +2975,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 +2996,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 +3005,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 +3022,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 +3046,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 +3057,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; @@ -3084,7 +3103,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) { @@ -3104,7 +3123,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) { @@ -3137,7 +3156,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } } 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; } @@ -3157,7 +3176,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\ @@ -3177,6 +3196,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\ @@ -3472,7 +3493,7 @@ static int array_modexec(PyObject *m) { array_state *state = get_array_state(m); - char buffer[Py_ARRAY_LENGTH(descriptors)], *p; + char buffer[Py_ARRAY_LENGTH(descriptors)*2], *p; PyObject *typecodes; const struct arraydescr *descr; @@ -3512,8 +3533,9 @@ array_modexec(PyObject *m) } p = buffer; - for (descr = descriptors; descr->typecode != '\0'; descr++) { - *p++ = (char)descr->typecode; + for (descr = descriptors; descr->typecode != NULL; descr++) { + strcpy(p, descr->typecode); + p += strlen(descr->typecode); } typecodes = PyUnicode_DecodeASCII(buffer, p - buffer, NULL); if (PyModule_Add(m, "typecodes", typecodes) < 0) { diff --git a/Modules/clinic/arraymodule.c.h b/Modules/clinic/arraymodule.c.h index 8a3fb4b515e4ac..eec47ab2b1f9e1 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 d0f414f7c4209b..9eba8693c2f0df 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);