From 0a6df009f15614433b04a5640f5167a1d99245ff Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 3 Sep 2016 17:21:29 +0100 Subject: Issue #11734: Add support for IEEE 754 half-precision floats to the struct module. Original patch by Eli Stevens. --- Modules/_struct.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) (limited to 'Modules/_struct.c') diff --git a/Modules/_struct.c b/Modules/_struct.c index df81900d6d..2bcd492a29 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -266,6 +266,33 @@ get_size_t(PyObject *v, size_t *p) /* Floating point helpers */ +static PyObject * +unpack_halffloat(const char *p, /* start of 2-byte string */ + int le) /* true for little-endian, false for big-endian */ +{ + double x; + + x = _PyFloat_Unpack2((unsigned char *)p, le); + if (x == -1.0 && PyErr_Occurred()) { + return NULL; + } + return PyFloat_FromDouble(x); +} + +static int +pack_halffloat(char *p, /* start of 2-byte string */ + PyObject *v, /* value to pack */ + int le) /* true for little-endian, false for big-endian */ +{ + double x = PyFloat_AsDouble(v); + if (x == -1.0 && PyErr_Occurred()) { + PyErr_SetString(StructError, + "required argument is not a float"); + return -1; + } + return _PyFloat_Pack2(x, (unsigned char *)p, le); +} + static PyObject * unpack_float(const char *p, /* start of 4-byte string */ int le) /* true for little-endian, false for big-endian */ @@ -469,6 +496,16 @@ nu_bool(const char *p, const formatdef *f) } +static PyObject * +nu_halffloat(const char *p, const formatdef *f) +{ +#if PY_LITTLE_ENDIAN + return unpack_halffloat(p, 1); +#else + return unpack_halffloat(p, 0); +#endif +} + static PyObject * nu_float(const char *p, const formatdef *f) { @@ -680,6 +717,16 @@ np_bool(char *p, PyObject *v, const formatdef *f) return 0; } +static int +np_halffloat(char *p, PyObject *v, const formatdef *f) +{ +#if PY_LITTLE_ENDIAN + return pack_halffloat(p, v, 1); +#else + return pack_halffloat(p, v, 0); +#endif +} + static int np_float(char *p, PyObject *v, const formatdef *f) { @@ -743,6 +790,7 @@ static const formatdef native_table[] = { {'Q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_ulonglong,np_ulonglong}, #endif {'?', sizeof(BOOL_TYPE), BOOL_ALIGN, nu_bool, np_bool}, + {'e', sizeof(short), SHORT_ALIGN, nu_halffloat, np_halffloat}, {'f', sizeof(float), FLOAT_ALIGN, nu_float, np_float}, {'d', sizeof(double), DOUBLE_ALIGN, nu_double, np_double}, {'P', sizeof(void *), VOID_P_ALIGN, nu_void_p, np_void_p}, @@ -825,6 +873,12 @@ bu_ulonglong(const char *p, const formatdef *f) #endif } +static PyObject * +bu_halffloat(const char *p, const formatdef *f) +{ + return unpack_halffloat(p, 0); +} + static PyObject * bu_float(const char *p, const formatdef *f) { @@ -921,6 +975,12 @@ bp_ulonglong(char *p, PyObject *v, const formatdef *f) return res; } +static int +bp_halffloat(char *p, PyObject *v, const formatdef *f) +{ + return pack_halffloat(p, v, 0); +} + static int bp_float(char *p, PyObject *v, const formatdef *f) { @@ -972,6 +1032,7 @@ static formatdef bigendian_table[] = { {'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}, {0} @@ -1053,6 +1114,12 @@ lu_ulonglong(const char *p, const formatdef *f) #endif } +static PyObject * +lu_halffloat(const char *p, const formatdef *f) +{ + return unpack_halffloat(p, 1); +} + static PyObject * lu_float(const char *p, const formatdef *f) { @@ -1141,6 +1208,12 @@ lp_ulonglong(char *p, PyObject *v, const formatdef *f) return res; } +static int +lp_halffloat(char *p, PyObject *v, const formatdef *f) +{ + return pack_halffloat(p, v, 1); +} + static int lp_float(char *p, PyObject *v, const formatdef *f) { @@ -1182,6 +1255,7 @@ static formatdef lilendian_table[] = { {'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}, {0} @@ -2239,7 +2313,7 @@ these can be preceded by a decimal repeat count:\n\ x: pad byte (no data); c:char; b:signed byte; B:unsigned byte;\n\ ?: _Bool (requires C99; if not available, char is used instead)\n\ h:short; H:unsigned short; i:int; I:unsigned int;\n\ - l:long; L:unsigned long; f:float; d:double.\n\ + l:long; L:unsigned long; f:float; d:double; e:half-float.\n\ Special cases (preceding decimal count indicates length):\n\ s:string (array of char); p: pascal string (with count byte).\n\ Special cases (only available in native format):\n\ -- cgit v1.2.1 From 907ccded8b6fa68f6f572ae5de6760595df0d5a1 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 5 Sep 2016 17:44:18 -0700 Subject: require a long long data type (closes #27961) --- Modules/_struct.c | 43 ------------------------------------------- 1 file changed, 43 deletions(-) (limited to 'Modules/_struct.c') diff --git a/Modules/_struct.c b/Modules/_struct.c index 2bcd492a29..ba60ba6133 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -71,10 +71,8 @@ typedef struct { char c; size_t x; } st_size_t; /* We can't support q and Q in native mode unless the compiler does; in std mode, they're 8 bytes on all platforms. */ -#ifdef HAVE_LONG_LONG typedef struct { char c; PY_LONG_LONG x; } s_long_long; #define LONG_LONG_ALIGN (sizeof(s_long_long) - sizeof(PY_LONG_LONG)) -#endif #ifdef HAVE_C99_BOOL #define BOOL_TYPE _Bool @@ -164,8 +162,6 @@ get_ulong(PyObject *v, unsigned long *p) return 0; } -#ifdef HAVE_LONG_LONG - /* Same, but handling native long long. */ static int @@ -212,8 +208,6 @@ get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p) return 0; } -#endif - /* Same, but handling Py_ssize_t */ static int @@ -463,8 +457,6 @@ nu_size_t(const char *p, const formatdef *f) /* Native mode doesn't support q or Q unless the platform C supports long long (or, on Windows, __int64). */ -#ifdef HAVE_LONG_LONG - static PyObject * nu_longlong(const char *p, const formatdef *f) { @@ -485,8 +477,6 @@ nu_ulonglong(const char *p, const formatdef *f) return PyLong_FromUnsignedLongLong(x); } -#endif - static PyObject * nu_bool(const char *p, const formatdef *f) { @@ -680,8 +670,6 @@ np_size_t(char *p, PyObject *v, const formatdef *f) return 0; } -#ifdef HAVE_LONG_LONG - static int np_longlong(char *p, PyObject *v, const formatdef *f) { @@ -701,7 +689,6 @@ np_ulonglong(char *p, PyObject *v, const formatdef *f) memcpy(p, (char *)&x, sizeof x); return 0; } -#endif static int @@ -785,10 +772,8 @@ static const formatdef native_table[] = { {'L', sizeof(long), LONG_ALIGN, nu_ulong, np_ulong}, {'n', sizeof(size_t), SIZE_T_ALIGN, nu_ssize_t, np_ssize_t}, {'N', sizeof(size_t), SIZE_T_ALIGN, nu_size_t, np_size_t}, -#ifdef HAVE_LONG_LONG {'q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_longlong, np_longlong}, {'Q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_ulonglong,np_ulonglong}, -#endif {'?', sizeof(BOOL_TYPE), BOOL_ALIGN, nu_bool, np_bool}, {'e', sizeof(short), SHORT_ALIGN, nu_halffloat, np_halffloat}, {'f', sizeof(float), FLOAT_ALIGN, nu_float, np_float}, @@ -831,7 +816,6 @@ bu_uint(const char *p, const formatdef *f) static PyObject * bu_longlong(const char *p, const formatdef *f) { -#ifdef HAVE_LONG_LONG PY_LONG_LONG x = 0; Py_ssize_t i = f->size; const unsigned char *bytes = (const unsigned char *)p; @@ -844,18 +828,11 @@ bu_longlong(const char *p, const formatdef *f) if (x >= LONG_MIN && x <= LONG_MAX) return PyLong_FromLong(Py_SAFE_DOWNCAST(x, PY_LONG_LONG, long)); return PyLong_FromLongLong(x); -#else - return _PyLong_FromByteArray((const unsigned char *)p, - 8, - 0, /* little-endian */ - 1 /* signed */); -#endif } static PyObject * bu_ulonglong(const char *p, const formatdef *f) { -#ifdef HAVE_LONG_LONG unsigned PY_LONG_LONG x = 0; Py_ssize_t i = f->size; const unsigned char *bytes = (const unsigned char *)p; @@ -865,12 +842,6 @@ bu_ulonglong(const char *p, const formatdef *f) if (x <= LONG_MAX) return PyLong_FromLong(Py_SAFE_DOWNCAST(x, unsigned PY_LONG_LONG, long)); return PyLong_FromUnsignedLongLong(x); -#else - return _PyLong_FromByteArray((const unsigned char *)p, - 8, - 0, /* little-endian */ - 0 /* signed */); -#endif } static PyObject * @@ -1072,7 +1043,6 @@ lu_uint(const char *p, const formatdef *f) static PyObject * lu_longlong(const char *p, const formatdef *f) { -#ifdef HAVE_LONG_LONG PY_LONG_LONG x = 0; Py_ssize_t i = f->size; const unsigned char *bytes = (const unsigned char *)p; @@ -1085,18 +1055,11 @@ lu_longlong(const char *p, const formatdef *f) if (x >= LONG_MIN && x <= LONG_MAX) return PyLong_FromLong(Py_SAFE_DOWNCAST(x, PY_LONG_LONG, long)); return PyLong_FromLongLong(x); -#else - return _PyLong_FromByteArray((const unsigned char *)p, - 8, - 1, /* little-endian */ - 1 /* signed */); -#endif } static PyObject * lu_ulonglong(const char *p, const formatdef *f) { -#ifdef HAVE_LONG_LONG unsigned PY_LONG_LONG x = 0; Py_ssize_t i = f->size; const unsigned char *bytes = (const unsigned char *)p; @@ -1106,12 +1069,6 @@ lu_ulonglong(const char *p, const formatdef *f) if (x <= LONG_MAX) return PyLong_FromLong(Py_SAFE_DOWNCAST(x, unsigned PY_LONG_LONG, long)); return PyLong_FromUnsignedLongLong(x); -#else - return _PyLong_FromByteArray((const unsigned char *)p, - 8, - 1, /* little-endian */ - 0 /* signed */); -#endif } static PyObject * -- cgit v1.2.1 From 914b59533a094e815809f62441d02b560e23057b Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 6 Sep 2016 10:46:49 -0700 Subject: replace PY_LONG_LONG with long long --- Modules/_struct.c | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'Modules/_struct.c') diff --git a/Modules/_struct.c b/Modules/_struct.c index ba60ba6133..a601f03ce7 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -71,8 +71,8 @@ typedef struct { char c; size_t x; } st_size_t; /* We can't support q and Q in native mode unless the compiler does; in std mode, they're 8 bytes on all platforms. */ -typedef struct { char c; PY_LONG_LONG x; } s_long_long; -#define LONG_LONG_ALIGN (sizeof(s_long_long) - sizeof(PY_LONG_LONG)) +typedef struct { char c; long long x; } s_long_long; +#define LONG_LONG_ALIGN (sizeof(s_long_long) - sizeof(long long)) #ifdef HAVE_C99_BOOL #define BOOL_TYPE _Bool @@ -165,9 +165,9 @@ get_ulong(PyObject *v, unsigned long *p) /* Same, but handling native long long. */ static int -get_longlong(PyObject *v, PY_LONG_LONG *p) +get_longlong(PyObject *v, long long *p) { - PY_LONG_LONG x; + long long x; v = get_pylong(v); if (v == NULL) @@ -175,7 +175,7 @@ get_longlong(PyObject *v, PY_LONG_LONG *p) assert(PyLong_Check(v)); x = PyLong_AsLongLong(v); Py_DECREF(v); - if (x == (PY_LONG_LONG)-1 && PyErr_Occurred()) { + if (x == (long long)-1 && PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) PyErr_SetString(StructError, "argument out of range"); @@ -188,9 +188,9 @@ get_longlong(PyObject *v, PY_LONG_LONG *p) /* Same, but handling native unsigned long long. */ static int -get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p) +get_ulonglong(PyObject *v, unsigned long long *p) { - unsigned PY_LONG_LONG x; + unsigned long long x; v = get_pylong(v); if (v == NULL) @@ -198,7 +198,7 @@ get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p) assert(PyLong_Check(v)); x = PyLong_AsUnsignedLongLong(v); Py_DECREF(v); - if (x == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) { + if (x == (unsigned long long)-1 && PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) PyErr_SetString(StructError, "argument out of range"); @@ -460,20 +460,20 @@ nu_size_t(const char *p, const formatdef *f) static PyObject * nu_longlong(const char *p, const formatdef *f) { - PY_LONG_LONG x; + long long x; memcpy((char *)&x, p, sizeof x); if (x >= LONG_MIN && x <= LONG_MAX) - return PyLong_FromLong(Py_SAFE_DOWNCAST(x, PY_LONG_LONG, long)); + return PyLong_FromLong(Py_SAFE_DOWNCAST(x, long long, long)); return PyLong_FromLongLong(x); } static PyObject * nu_ulonglong(const char *p, const formatdef *f) { - unsigned PY_LONG_LONG x; + unsigned long long x; memcpy((char *)&x, p, sizeof x); if (x <= LONG_MAX) - return PyLong_FromLong(Py_SAFE_DOWNCAST(x, unsigned PY_LONG_LONG, long)); + return PyLong_FromLong(Py_SAFE_DOWNCAST(x, unsigned long long, long)); return PyLong_FromUnsignedLongLong(x); } @@ -673,7 +673,7 @@ np_size_t(char *p, PyObject *v, const formatdef *f) static int np_longlong(char *p, PyObject *v, const formatdef *f) { - PY_LONG_LONG x; + long long x; if (get_longlong(v, &x) < 0) return -1; memcpy(p, (char *)&x, sizeof x); @@ -683,7 +683,7 @@ np_longlong(char *p, PyObject *v, const formatdef *f) static int np_ulonglong(char *p, PyObject *v, const formatdef *f) { - unsigned PY_LONG_LONG x; + unsigned long long x; if (get_ulonglong(v, &x) < 0) return -1; memcpy(p, (char *)&x, sizeof x); @@ -772,8 +772,8 @@ static const formatdef native_table[] = { {'L', sizeof(long), LONG_ALIGN, nu_ulong, np_ulong}, {'n', sizeof(size_t), SIZE_T_ALIGN, nu_ssize_t, np_ssize_t}, {'N', sizeof(size_t), SIZE_T_ALIGN, nu_size_t, np_size_t}, - {'q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_longlong, np_longlong}, - {'Q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_ulonglong,np_ulonglong}, + {'q', sizeof(long long), LONG_LONG_ALIGN, nu_longlong, np_longlong}, + {'Q', sizeof(long long), LONG_LONG_ALIGN, nu_ulonglong,np_ulonglong}, {'?', sizeof(BOOL_TYPE), BOOL_ALIGN, nu_bool, np_bool}, {'e', sizeof(short), SHORT_ALIGN, nu_halffloat, np_halffloat}, {'f', sizeof(float), FLOAT_ALIGN, nu_float, np_float}, @@ -816,7 +816,7 @@ bu_uint(const char *p, const formatdef *f) static PyObject * bu_longlong(const char *p, const formatdef *f) { - PY_LONG_LONG x = 0; + long long x = 0; Py_ssize_t i = f->size; const unsigned char *bytes = (const unsigned char *)p; do { @@ -824,23 +824,23 @@ bu_longlong(const char *p, const formatdef *f) } while (--i > 0); /* Extend the sign bit. */ if (SIZEOF_LONG_LONG > f->size) - x |= -(x & ((PY_LONG_LONG)1 << ((8 * f->size) - 1))); + x |= -(x & ((long long)1 << ((8 * f->size) - 1))); if (x >= LONG_MIN && x <= LONG_MAX) - return PyLong_FromLong(Py_SAFE_DOWNCAST(x, PY_LONG_LONG, long)); + return PyLong_FromLong(Py_SAFE_DOWNCAST(x, long long, long)); return PyLong_FromLongLong(x); } static PyObject * bu_ulonglong(const char *p, const formatdef *f) { - unsigned PY_LONG_LONG x = 0; + unsigned long long x = 0; Py_ssize_t i = f->size; const unsigned char *bytes = (const unsigned char *)p; do { x = (x<<8) | *bytes++; } while (--i > 0); if (x <= LONG_MAX) - return PyLong_FromLong(Py_SAFE_DOWNCAST(x, unsigned PY_LONG_LONG, long)); + return PyLong_FromLong(Py_SAFE_DOWNCAST(x, unsigned long long, long)); return PyLong_FromUnsignedLongLong(x); } @@ -1043,7 +1043,7 @@ lu_uint(const char *p, const formatdef *f) static PyObject * lu_longlong(const char *p, const formatdef *f) { - PY_LONG_LONG x = 0; + long long x = 0; Py_ssize_t i = f->size; const unsigned char *bytes = (const unsigned char *)p; do { @@ -1051,23 +1051,23 @@ lu_longlong(const char *p, const formatdef *f) } while (i > 0); /* Extend the sign bit. */ if (SIZEOF_LONG_LONG > f->size) - x |= -(x & ((PY_LONG_LONG)1 << ((8 * f->size) - 1))); + x |= -(x & ((long long)1 << ((8 * f->size) - 1))); if (x >= LONG_MIN && x <= LONG_MAX) - return PyLong_FromLong(Py_SAFE_DOWNCAST(x, PY_LONG_LONG, long)); + return PyLong_FromLong(Py_SAFE_DOWNCAST(x, long long, long)); return PyLong_FromLongLong(x); } static PyObject * lu_ulonglong(const char *p, const formatdef *f) { - unsigned PY_LONG_LONG x = 0; + unsigned long long x = 0; Py_ssize_t i = f->size; const unsigned char *bytes = (const unsigned char *)p; do { x = (x<<8) | bytes[--i]; } while (i > 0); if (x <= LONG_MAX) - return PyLong_FromLong(Py_SAFE_DOWNCAST(x, unsigned PY_LONG_LONG, long)); + return PyLong_FromLong(Py_SAFE_DOWNCAST(x, unsigned long long, long)); return PyLong_FromUnsignedLongLong(x); } -- cgit v1.2.1 From 06c2472156a7ab7650b0683f656b2a71193f2952 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 7 Sep 2016 11:06:17 -0700 Subject: require C99 bool --- Modules/_struct.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'Modules/_struct.c') diff --git a/Modules/_struct.c b/Modules/_struct.c index a601f03ce7..082096186e 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -60,6 +60,7 @@ typedef struct { char c; float x; } st_float; typedef struct { char c; double x; } st_double; typedef struct { char c; void *x; } st_void_p; typedef struct { char c; size_t x; } st_size_t; +typedef struct { char c; _Bool x; } st_bool; #define SHORT_ALIGN (sizeof(st_short) - sizeof(short)) #define INT_ALIGN (sizeof(st_int) - sizeof(int)) @@ -68,21 +69,13 @@ typedef struct { char c; size_t x; } st_size_t; #define DOUBLE_ALIGN (sizeof(st_double) - sizeof(double)) #define VOID_P_ALIGN (sizeof(st_void_p) - sizeof(void *)) #define SIZE_T_ALIGN (sizeof(st_size_t) - sizeof(size_t)) +#define BOOL_ALIGN (sizeof(st_bool) - sizeof(_Bool)) /* We can't support q and Q in native mode unless the compiler does; in std mode, they're 8 bytes on all platforms. */ typedef struct { char c; long long x; } s_long_long; #define LONG_LONG_ALIGN (sizeof(s_long_long) - sizeof(long long)) -#ifdef HAVE_C99_BOOL -#define BOOL_TYPE _Bool -typedef struct { char c; _Bool x; } s_bool; -#define BOOL_ALIGN (sizeof(s_bool) - sizeof(BOOL_TYPE)) -#else -#define BOOL_TYPE char -#define BOOL_ALIGN 0 -#endif - #ifdef __powerc #pragma options align=reset #endif @@ -480,7 +473,7 @@ nu_ulonglong(const char *p, const formatdef *f) static PyObject * nu_bool(const char *p, const formatdef *f) { - BOOL_TYPE x; + _Bool x; memcpy((char *)&x, p, sizeof x); return PyBool_FromLong(x != 0); } @@ -695,7 +688,7 @@ static int np_bool(char *p, PyObject *v, const formatdef *f) { int y; - BOOL_TYPE x; + _Bool x; y = PyObject_IsTrue(v); if (y < 0) return -1; @@ -774,7 +767,7 @@ static const formatdef native_table[] = { {'N', sizeof(size_t), SIZE_T_ALIGN, nu_size_t, np_size_t}, {'q', sizeof(long long), LONG_LONG_ALIGN, nu_longlong, np_longlong}, {'Q', sizeof(long long), LONG_LONG_ALIGN, nu_ulonglong,np_ulonglong}, - {'?', sizeof(BOOL_TYPE), BOOL_ALIGN, nu_bool, np_bool}, + {'?', sizeof(_Bool), BOOL_ALIGN, nu_bool, np_bool}, {'e', sizeof(short), SHORT_ALIGN, nu_halffloat, np_halffloat}, {'f', sizeof(float), FLOAT_ALIGN, nu_float, np_float}, {'d', sizeof(double), DOUBLE_ALIGN, nu_double, np_double}, -- cgit v1.2.1