support arbitrarily sized typedef integral types
authorLisandro Dalcin <dalcinl@gmail.com>
Fri, 23 Apr 2010 16:02:07 +0000 (13:02 -0300)
committerLisandro Dalcin <dalcinl@gmail.com>
Fri, 23 Apr 2010 16:02:07 +0000 (13:02 -0300)
Cython/Compiler/PyrexTypes.py
tests/run/cpp_nonstdint.h [new file with mode: 0644]
tests/run/cpp_nonstdint.pyx [new file with mode: 0644]

index 83005922cb0fc58dc3caa8cca9228c02bc50957f..37e1cd0446711599b630a09fb8c4a65aeaa9eb99 100755 (executable)
@@ -721,8 +721,8 @@ static CYTHON_INLINE %(type)s __Pyx_PyInt_from_py_%(TypeName)s(PyObject *);
 """,
 impl="""
 static CYTHON_INLINE %(type)s __Pyx_PyInt_from_py_%(TypeName)s(PyObject* x) {
-    const %(type)s neg_one = (%(type)s)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
+    const %(type)s neg_one = (%(type)s)-1, const_zero = (%(type)s)0;
+    const int is_unsigned = const_zero < neg_one;
     if (sizeof(%(type)s) == sizeof(char)) {
         if (is_unsigned)
             return (%(type)s)__Pyx_PyInt_AsUnsignedChar(x);
@@ -748,17 +748,28 @@ static CYTHON_INLINE %(type)s __Pyx_PyInt_from_py_%(TypeName)s(PyObject* x) {
             return (%(type)s)__Pyx_PyInt_AsUnsignedLongLong(x);
         else
             return (%(type)s)__Pyx_PyInt_AsSignedLongLong(x);
-#if 0
-    } else if (sizeof(%(type)s) > sizeof(short) &&
-               sizeof(%(type)s) < sizeof(int)) { /*  __int32 ILP64 ? */
-        if (is_unsigned)
-            return (%(type)s)__Pyx_PyInt_AsUnsignedInt(x);
-        else
-            return (%(type)s)__Pyx_PyInt_AsSignedInt(x);
-#endif
+    }  else {
+        %(type)s val;
+        PyObject *v = __Pyx_PyNumber_Int(x);
+        #if PY_VERSION_HEX < 0x03000000
+        if (likely(v) && !PyLong_Check(v)) {
+            PyObject *tmp = v;
+            v = PyNumber_Long(tmp);
+            Py_DECREF(tmp);
+        }
+        #endif
+        if (likely(v)) {
+            int one = 1; int is_little = (int)*(unsigned char *)&one;
+            unsigned char *bytes = (unsigned char *)&val;
+            int ret = _PyLong_AsByteArray((PyLongObject *)v,
+                                          bytes, sizeof(val),
+                                          is_little, !is_unsigned);
+            Py_DECREF(v);
+            if (likely(!ret))
+                return val;
+        }
+        return (%(type)s)-1;
     }
-    PyErr_SetString(PyExc_TypeError, "%(TypeName)s");
-    return (%(type)s)-1;
 }
 """)
 
@@ -768,20 +779,27 @@ static CYTHON_INLINE PyObject *__Pyx_PyInt_to_py_%(TypeName)s(%(type)s);
 """,
 impl="""
 static CYTHON_INLINE PyObject *__Pyx_PyInt_to_py_%(TypeName)s(%(type)s val) {
-    const %(type)s neg_one = (%(type)s)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-    if (sizeof(%(type)s) <  sizeof(long)) {
+    const %(type)s neg_one = (%(type)s)-1, const_zero = (%(type)s)0;
+    const int is_unsigned = const_zero < neg_one;
+    if ((sizeof(%(type)s) == sizeof(char))  ||
+        (sizeof(%(type)s) == sizeof(short))) {
         return PyInt_FromLong((long)val);
-    } else if (sizeof(%(type)s) == sizeof(long)) {
+    } else if ((sizeof(%(type)s) == sizeof(int)) ||
+               (sizeof(%(type)s) == sizeof(long))) {
         if (is_unsigned)
             return PyLong_FromUnsignedLong((unsigned long)val);
         else
             return PyInt_FromLong((long)val);
-    } else { /* (sizeof(%(type)s) > sizeof(long)) */
+    } else if (sizeof(%(type)s) == sizeof(PY_LONG_LONG)) {
         if (is_unsigned)
             return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)val);
         else
             return PyLong_FromLongLong((PY_LONG_LONG)val);
+    } else {
+        int one = 1; int little = (int)*(unsigned char *)&one;
+        unsigned char *bytes = (unsigned char *)&val;
+        return _PyLong_FromByteArray(bytes, sizeof(%(type)s), 
+                                     little, !is_unsigned);
     }
 }
 """)
diff --git a/tests/run/cpp_nonstdint.h b/tests/run/cpp_nonstdint.h
new file mode 100644 (file)
index 0000000..480115a
--- /dev/null
@@ -0,0 +1,89 @@
+// -*- c++ -*-
+#include <stdio.h>
+template<unsigned int N>
+class Integral {
+
+  unsigned char bytes[N];
+
+ public:
+  Integral() {
+    for (unsigned int i=0; i<N; i++)
+      bytes[i] = 0;
+  }
+  Integral(const Integral &I) {
+    for (unsigned int i=0; i<N; i++)
+      bytes[i] = I.bytes[i];
+  }
+  Integral(signed char I) {
+    unsigned char p = (I<0) ? 0xFF : 0x00;
+    for (unsigned int i=0; i<N; i++)
+      bytes[i] = p;
+    bytes[lsb()] = *(unsigned char*)&I;
+  }
+
+  operator signed char() const {
+    return *(signed char*)&bytes[lsb()];
+  }
+  Integral& operator=(const Integral &I) {
+    for (unsigned int i=0; i<N; i++)
+      bytes[i] = I.bytes[i];
+    return *this;
+  }
+  bool operator<(const Integral &I) const
+  { return cmp(I) < 0; }
+  bool operator>(const Integral &I) const
+  { return cmp(I) > 0; }
+  bool operator<=(const Integral &I) const
+  { return cmp(I) <= 0; }
+  bool operator>=(const Integral &I) const
+  { return cmp(I) >= 0; }
+  bool operator==(const Integral &I) const
+  { return cmp(I) == 0; }
+  bool operator!=(const Integral &I) const
+  { return cmp(I) != 0; }
+
+ private:
+  static bool is_le() {
+    int one = 1;
+    return (int)*(unsigned char *)&one;
+  }
+  static unsigned int lsb() {
+    return is_le() ? 0 : N-1;
+  }
+  static unsigned int msb() {
+    return is_le() ? N-1 : 0;
+  }
+  int cmp(const Integral& J) const {
+    const Integral& I = *this;
+    unsigned char sI = I.bytes[msb()] & 0x80;
+    unsigned char sJ = J.bytes[msb()] & 0x80;
+    if (sI > sJ) return -1;
+    if (sI < sJ) return +1;
+    unsigned char bI = I.bytes[msb()] & 0x7F;
+    unsigned char bJ = J.bytes[msb()] & 0x7F;
+    int cmpabs = 0;
+    if (bI < bJ)
+      cmpabs = -1;
+    else if (bI > bJ)
+      cmpabs = +1;
+    else {
+      int incr = is_le() ? -1 : 1;
+      unsigned int i = msb() + incr;
+      while (i != lsb()) {
+       if (I.bytes[i] < J.bytes[i])
+         { cmpabs = -1;  break; }
+       if (I.bytes[i] > J.bytes[i])
+         { cmpabs = +1;  break; }
+       i += incr;
+      }
+    }
+    if (sI) return -cmpabs;
+    else    return +cmpabs;
+  }
+
+};
+
+typedef Integral<3> Int24;
+typedef Integral<7> Int56;
+typedef Integral<11> Int88;
+typedef Integral<64> Int512;
diff --git a/tests/run/cpp_nonstdint.pyx b/tests/run/cpp_nonstdint.pyx
new file mode 100644 (file)
index 0000000..17c6a23
--- /dev/null
@@ -0,0 +1,149 @@
+cdef extern from "cpp_nonstdint.h":
+    ctypedef int Int24
+    ctypedef int Int56
+    ctypedef int Int88
+    ctypedef int Int512
+
+cdef object one = 1
+
+# ---
+
+INT24_MAX = (one<<(sizeof(Int24)*8-1))-one
+INT24_MIN = (-INT24_MAX-one)
+
+def test_int24(Int24 i):
+    """
+    >>> str(test_int24(-1))
+    '-1'
+    >>> str(test_int24(0))
+    '0'
+    >>> str(test_int24(1))
+    '1'
+
+    >>> test_int24(INT24_MAX) == INT24_MAX
+    True
+    >>> test_int24(INT24_MIN) == INT24_MIN
+    True
+
+    >>> test_int24(INT24_MIN-1) #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    OverflowError: ...
+    >>> test_int24(INT24_MAX+1) #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    OverflowError: ...
+
+    >>> test_int24("123") #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    TypeError: ...
+    """
+    return i
+
+# ---
+
+INT56_MAX = (one<<(sizeof(Int56)*8-1))-one
+INT56_MIN = (-INT56_MAX-one)
+
+def test_int56(Int56 i):
+    """
+    >>> str(test_int56(-1))
+    '-1'
+    >>> str(test_int56(0))
+    '0'
+    >>> str(test_int56(1))
+    '1'
+
+    >>> test_int56(INT56_MAX) == INT56_MAX
+    True
+    >>> test_int56(INT56_MIN) == INT56_MIN
+    True
+
+    >>> test_int56(INT56_MIN-1) #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    OverflowError: ...
+    >>> test_int56(INT56_MAX+1) #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    OverflowError: ...
+
+    >>> test_int56("123") #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    TypeError: ...
+    """
+    return i
+
+# ---
+
+INT88_MAX = (one<<(sizeof(Int88)*8-1))-one
+INT88_MIN = (-INT88_MAX-one)
+
+def test_int88(Int88 i):
+    """
+    >>> str(test_int88(-1))
+    '-1'
+    >>> str(test_int88(0))
+    '0'
+    >>> str(test_int88(1))
+    '1'
+
+    >>> test_int88(INT88_MAX) == INT88_MAX
+    True
+    >>> test_int88(INT88_MIN) == INT88_MIN
+    True
+
+    >>> test_int88(INT88_MIN-1) #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    OverflowError: ...
+    >>> test_int88(INT88_MAX+1) #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    OverflowError: ...
+
+    >>> test_int88("123") #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    TypeError: ...
+    """
+    return i
+
+# ---
+
+INT512_MAX = (one<<(sizeof(Int512)*8-1))-one
+INT512_MIN = (-INT512_MAX-one)
+
+def test_int512(Int512 i):
+    """
+    >>> str(test_int512(-1))
+    '-1'
+    >>> str(test_int512(0))
+    '0'
+    >>> str(test_int512(1))
+    '1'
+
+    >>> test_int512(INT512_MAX) == INT512_MAX
+    True
+    >>> test_int512(INT512_MIN) == INT512_MIN
+    True
+
+    >>> test_int512(INT512_MIN-1) #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    OverflowError: ...
+    >>> test_int512(INT512_MAX+1) #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    OverflowError: ...
+
+    >>> test_int512("123") #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    TypeError: ...
+    """
+    return i
+
+# ---