[pypy-commit] cffi default: Fix: the type char32_t is defined by the standard as always unsigned

arigo pypy.commits at gmail.com
Fri Jun 2 04:02:47 EDT 2017


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2965:c0c2020872d3
Date: 2017-06-02 10:02 +0200
http://bitbucket.org/cffi/cffi/changeset/c0c2020872d3/

Log:	Fix: the type char32_t is defined by the standard as always unsigned

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -143,6 +143,7 @@
 #define CT_IS_UNSIZED_CHAR_A   0x00800000
 #define CT_LAZY_FIELD_LIST     0x01000000
 #define CT_WITH_PACKED_CHANGE  0x02000000
+#define CT_IS_SIGNED_WCHAR     0x04000000
 #define CT_PRIMITIVE_ANY  (CT_PRIMITIVE_SIGNED |        \
                            CT_PRIMITIVE_UNSIGNED |      \
                            CT_PRIMITIVE_CHAR |          \
@@ -1591,8 +1592,8 @@
             return 0;
         }
         case 4: {
-            int res = _convert_to_char32_t(init);
-            if (res == -1 && PyErr_Occurred())
+            cffi_char32_t res = _convert_to_char32_t(init);
+            if (res == (cffi_char32_t)-1 && PyErr_Occurred())
                 return -1;
             *(cffi_char32_t *)data = res;
             return 0;
@@ -2084,9 +2085,12 @@
         case 2:
             return PyInt_FromLong((long)*(cffi_char16_t *)cd->c_data);
         case 4:
-            /* NB. cast via int32_t instead of cffi_char32_t, so that
-               we expose a signed result to the user */
-            return PyInt_FromLong((long)*(int32_t *)cd->c_data);
+            if (cd->c_type->ct_flags & CT_IS_SIGNED_WCHAR)
+                return PyInt_FromLong((long)*(int32_t *)cd->c_data);
+            else if (sizeof(long) > 4)
+                return PyInt_FromLong(*(uint32_t *)cd->c_data);
+            else
+                return PyLong_FromUnsignedLong(*(uint32_t *)cd->c_data);
         }
     }
     else if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
@@ -3687,8 +3691,15 @@
                          "cannot cast %s to ctype '%s'", err_buf, ct->ct_name);
             return NULL;
         }
-        /* the user sees char32_t being signed, but not char16_t */
-        value = (int32_t)ordinal;
+        /* the types char16_t and char32_t are both unsigned.  However,
+           wchar_t might be signed.  In theory it does not matter,
+           because 'ordinal' comes from a regular Python unicode. */
+#ifdef HAVE_WCHAR_H
+        if (ct->ct_flags & CT_IS_SIGNED_WCHAR)
+            value = (wchar_t)ordinal;
+        else
+#endif
+            value = ordinal;
     }
     else if (PyBytes_Check(ob)) {
         int res = _convert_to_char(ob);
@@ -3733,7 +3744,10 @@
             Py_DECREF(io);
             return -1;
         }
-        *out_value = (int32_t)ordinal;
+        /* the signness of the 32-bit version of wide chars should not
+         * matter here, because 'ordinal' comes from a normal Python
+         * unicode string */
+        *out_value = ordinal;
         return 1;
     }
     return 0;
@@ -4195,7 +4209,8 @@
 
 #ifdef HAVE_WCHAR_H
 # define ENUM_PRIMITIVE_TYPES_WCHAR                             \
-       EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR )
+       EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR |                  \
+                           (((wchar_t)-1) > 0 ? 0 : CT_IS_SIGNED_WCHAR))
 #else
 # define ENUM_PRIMITIVE_TYPES_WCHAR   /* nothing */
 #endif
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -2101,12 +2101,16 @@
     _test_wchar_variant("wchar_t")
 
 def test_char16():
-    assert sizeof(new_primitive_type("char16_t")) == 2
+    BChar16 = new_primitive_type("char16_t")
+    assert sizeof(BChar16) == 2
     _test_wchar_variant("char16_t")
+    assert int(cast(BChar16, -1)) == 0xffff       # always unsigned
 
 def test_char32():
-    assert sizeof(new_primitive_type("char32_t")) == 4
+    BChar32 = new_primitive_type("char32_t")
+    assert sizeof(BChar32) == 4
     _test_wchar_variant("char32_t")
+    assert int(cast(BChar32, -1)) == 0xffffffff   # always unsigned
 
 def _test_wchar_variant(typename):
     BWChar = new_primitive_type(typename)
diff --git a/c/wchar_helper.h b/c/wchar_helper.h
--- a/c/wchar_helper.h
+++ b/c/wchar_helper.h
@@ -4,7 +4,6 @@
 
 typedef uint16_t cffi_char16_t;
 typedef uint32_t cffi_char32_t;
-/* NB. cffi_char32_t is unsigned to make the logic here a bit easier */
 
 
 #if Py_UNICODE_SIZE == 2
diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h
--- a/cffi/_cffi_include.h
+++ b/cffi/_cffi_include.h
@@ -64,7 +64,7 @@
 # endif
 # if _MSC_VER < 1900 || !defined(__cplusplus)   /* MSVC < 2015, or plain C */
     typedef uint16_t char16_t;
-    typedef int32_t char32_t;
+    typedef uint32_t char32_t;
 # endif
 #else
 # include <stdint.h>


More information about the pypy-commit mailing list