Commits

Armin Rigo committed db148db Merge

hg merge default, and try to resolve all conflicts.
Not tested so far.

Comments (0)

Files changed (28)

 #endif
 
 #if PY_MAJOR_VERSION >= 3
+# define STR_OR_BYTES "bytes"
 # define PyText_Type PyUnicode_Type
 # define PyText_Check PyUnicode_Check
 # define PyText_FromFormat PyUnicode_FromFormat
 # define PyText_FromStringAndSize PyUnicode_FromStringAndSize
 # define PyText_InternInPlace PyUnicode_InternInPlace
 #else
+# define STR_OR_BYTES "str"
 # define PyText_Type PyString_Type
 # define PyText_Check PyString_Check
 # define PyText_FromFormat PyString_FromFormat
 #define CT_PRIMITIVE_SIGNED   1    /* signed integer */
 #define CT_PRIMITIVE_UNSIGNED 2    /* unsigned integer */
 #define CT_PRIMITIVE_CHAR     4    /* char, wchar_t */
-#define CT_PRIMITIVE_FLOAT    8    /* float, double */
+#define CT_PRIMITIVE_FLOAT    8    /* float, double, long double */
 #define CT_POINTER           16    /* pointer, excluding ptr-to-func */
 #define CT_ARRAY             32    /* array */
 #define CT_STRUCT            64    /* struct */
 #define CT_IS_ENUM               8192
 #define CT_IS_PTR_TO_OWNED      16384
 #define CT_CUSTOM_FIELD_POS     32768
+#define CT_IS_LONGDOUBLE        65536
 #define CT_PRIMITIVE_ANY  (CT_PRIMITIVE_SIGNED |        \
                            CT_PRIMITIVE_UNSIGNED |      \
                            CT_PRIMITIVE_CHAR |          \
 
     Py_ssize_t ct_size;     /* size of instances, or -1 if unknown */
     Py_ssize_t ct_length;   /* length of arrays, or -1 if unknown;
-                               or alignment of primitive and struct types */
+                               or alignment of primitive and struct types;
+                               always -1 for pointers */
     int ct_flags;           /* CT_xxx flags */
 
     int ct_name_position;   /* index in ct_name of where to put a var name */
     PyObject_HEAD
     CTypeDescrObject *c_type;
     char *c_data;
+    PyObject *c_weakreflist;
 } CDataObject;
 
 typedef struct cfieldobject_s {
     PyObject_HEAD
     CTypeDescrObject *cf_type;
     Py_ssize_t cf_offset;
-    short cf_bitshift, cf_bitsize;
+    short cf_bitshift;   /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */
+    short cf_bitsize;
     struct cfieldobject_s *cf_next;
 } CFieldObject;
+#define BS_REGULAR     (-1)      /* a regular field, not with bitshift */
+#define BS_EMPTY_ARRAY (-2)      /* a field which is an array 'type[0]' */
 
 static PyTypeObject CTypeDescr_Type;
 static PyTypeObject CField_Type;
     unsigned long long m_longlong;
     float m_float;
     double m_double;
+    long double m_longdouble;
 } union_alignment;
 
 typedef struct {
 
 typedef struct {
     CDataObject head;
-    PyObject *weakreflist;
-} CDataObject_own_base;
-
-typedef struct {
-    CDataObject_own_base head;
     union_alignment alignment;
 } CDataObject_own_nolength;
 
 typedef struct {
-    CDataObject_own_base head;
+    CDataObject head;
     Py_ssize_t length;
     union_alignment alignment;
 } CDataObject_own_length;
 
 typedef struct {
-    CDataObject_own_base head;
+    CDataObject head;
     PyObject *structobj;
 } CDataObject_own_structptr;
 
     }
 }
 
+static long double
+read_raw_longdouble_data(char *target)
+{
+    return *((long double*)target);
+}
+
 static void
 write_raw_float_data(char *target, double source, int size)
 {
         Py_FatalError("write_raw_float_data: bad float size");
 }
 
+static void
+write_raw_longdouble_data(char *target, long double source)
+{
+    *((long double*)target) = source;
+}
+
 static PyObject *
 new_simple_cdata(char *data, CTypeDescrObject *ct)
 {
     Py_INCREF(ct);
     cd->c_data = data;
     cd->c_type = ct;
+    cd->c_weakreflist = NULL;
     return (PyObject *)cd;
 }
 
 static PyObject *convert_enum_string_to_int(CTypeDescrObject *ct, PyObject *ob)
 {
     PyObject *d_value;
-
-    if (PyText_AsUTF8(ob)[0] == '#') {
-        char *number = PyText_AsUTF8(ob) + 1;   /* strip initial '#' */
+    char *p = PyText_AsUTF8(ob);
+
+    if (p[0] == '#') {
+        char *number = p + 1;       /* strip initial '#' */
         PyObject *ob2 = PyText_FromString(number);
         if (ob2 == NULL)
             return NULL;
     return d_value;
 }
 
+static CDataObject *_new_casted_primitive(CTypeDescrObject *ct);  /*forward*/
+
 static PyObject *
 convert_to_object(char *data, CTypeDescrObject *ct)
 {
                          ct->ct_name);
             return NULL;
         }
-        else if (ct->ct_flags & (CT_ARRAY|CT_STRUCT|CT_UNION)) {
+        else if (ct->ct_flags & (CT_STRUCT|CT_UNION)) {
+            return new_simple_cdata(data, ct);
+        }
+        else if (ct->ct_flags & CT_ARRAY) {
+            if (ct->ct_length < 0) {
+                /* we can't return a <cdata 'int[]'> here, because we don't
+                   know the length to give it.  As a compromize, returns
+                   <cdata 'int *'> in this case. */
+                ct = (CTypeDescrObject *)ct->ct_stuff;
+            }
             return new_simple_cdata(data, ct);
         }
     }
             return PyLong_FromUnsignedLongLong(value);
     }
     else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) {
-        double value = read_raw_float_data(data, ct->ct_size);
-        return PyFloat_FromDouble(value);
+        if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) {
+            double value = read_raw_float_data(data, ct->ct_size);
+            return PyFloat_FromDouble(value);
+        }
+        else {
+            long double value = read_raw_longdouble_data(data);
+            CDataObject *cd = _new_casted_primitive(ct);
+            if (cd != NULL)
+                write_raw_longdouble_data(cd->c_data, value);
+            return (PyObject *)cd;
+        }
     }
     else if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
         if (ct->ct_size == sizeof(char))
 }
 
 static int
+convert_array_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
+{
+    /* used by convert_from_object(), and also to decode lists/tuples/unicodes
+       passed as function arguments.  'ct' is an CT_ARRAY in the first case
+       and a CT_POINTER in the second case. */
+    const char *expected;
+    CTypeDescrObject *ctitem = ct->ct_itemdescr;
+
+    if (PyList_Check(init) || PyTuple_Check(init)) {
+        PyObject **items;
+        Py_ssize_t i, n;
+        n = PySequence_Fast_GET_SIZE(init);
+        if (ct->ct_length >= 0 && n > ct->ct_length) {
+            PyErr_Format(PyExc_IndexError,
+                         "too many initializers for '%s' (got %zd)",
+                         ct->ct_name, n);
+            return -1;
+        }
+        items = PySequence_Fast_ITEMS(init);
+        for (i=0; i<n; i++) {
+            if (convert_from_object(data, ctitem, items[i]) < 0)
+                return -1;
+            data += ctitem->ct_size;
+        }
+        return 0;
+    }
+    else if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) {
+        if (ctitem->ct_size == sizeof(char)) {
+            char *srcdata;
+            Py_ssize_t n;
+            if (!PyBytes_Check(init)) {
+                expected = STR_OR_BYTES" or list or tuple";
+                goto cannot_convert;
+            }
+            n = PyString_GET_SIZE(init);
+            if (ct->ct_length >= 0 && n > ct->ct_length) {
+                PyErr_Format(PyExc_IndexError,
+                             "initializer "STR_OR_BYTES" is too long for '%s' "
+                             "(got %zd characters)", ct->ct_name, n);
+                return -1;
+            }
+            if (n != ct->ct_length)
+                n++;
+            srcdata = PyString_AS_STRING(init);
+            memcpy(data, srcdata, n);
+            return 0;
+        }
+#ifdef HAVE_WCHAR_H
+        else {
+            Py_ssize_t n;
+            if (!PyUnicode_Check(init)) {
+                expected = "unicode or list or tuple";
+                goto cannot_convert;
+            }
+            n = _my_PyUnicode_SizeAsWideChar(init);
+            if (ct->ct_length >= 0 && n > ct->ct_length) {
+                PyErr_Format(PyExc_IndexError,
+                             "initializer unicode is too long for '%s' "
+                             "(got %zd characters)", ct->ct_name, n);
+                return -1;
+            }
+            if (n != ct->ct_length)
+                n++;
+            _my_PyUnicode_AsWideChar(init, (wchar_t *)data, n);
+            return 0;
+        }
+#endif
+    }
+    else {
+        expected = "list or tuple";
+        goto cannot_convert;
+    }
+
+ cannot_convert:
+    return _convert_error(init, ct->ct_name, expected);
+}
+
+static int
 convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
 {
     const char *expected;
     char buf[sizeof(PY_LONG_LONG)];
 
     if (ct->ct_flags & CT_ARRAY) {
-        CTypeDescrObject *ctitem = ct->ct_itemdescr;
-
-        if (PyList_Check(init) || PyTuple_Check(init)) {
-            PyObject **items;
-            Py_ssize_t i, n;
-            n = PySequence_Fast_GET_SIZE(init);
-            if (ct->ct_length >= 0 && n > ct->ct_length) {
-                PyErr_Format(PyExc_IndexError,
-                             "too many initializers for '%s' (got %zd)",
-                             ct->ct_name, n);
-                return -1;
-            }
-            items = PySequence_Fast_ITEMS(init);
-            for (i=0; i<n; i++) {
-                if (convert_from_object(data, ctitem, items[i]) < 0)
-                    return -1;
-                data += ctitem->ct_size;
-            }
-            return 0;
-        }
-        else if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) {
-            if (ctitem->ct_size == sizeof(char)) {
-                char *srcdata;
-                Py_ssize_t n;
-                if (!PyBytes_Check(init)) {
-                    expected = "bytes or list or tuple";
-                    goto cannot_convert;
-                }
-                n = PyBytes_GET_SIZE(init);
-                if (ct->ct_length >= 0 && n > ct->ct_length) {
-                    PyErr_Format(PyExc_IndexError,
-                                 "initializer string is too long for '%s' "
-                                 "(got %zd characters)", ct->ct_name, n);
-                    return -1;
-                }
-                if (n != ct->ct_length)
-                    n++;
-                srcdata = PyBytes_AS_STRING(init);
-                memcpy(data, srcdata, n);
-                return 0;
-            }
-#ifdef HAVE_WCHAR_H
-            else {
-                Py_ssize_t n;
-                if (!PyUnicode_Check(init)) {
-                    expected = "unicode or list or tuple";
-                    goto cannot_convert;
-                }
-                n = _my_PyUnicode_SizeAsWideChar(init);
-                if (ct->ct_length >= 0 && n > ct->ct_length) {
-                    PyErr_Format(PyExc_IndexError,
-                                 "initializer unicode is too long for '%s' "
-                                 "(got %zd characters)", ct->ct_name, n);
-                    return -1;
-                }
-                if (n != ct->ct_length)
-                    n++;
-                _my_PyUnicode_AsWideChar(init, (wchar_t *)data, n);
-                return 0;
-            }
-#endif
-        }
-        else {
-            expected = "list or tuple";
-            goto cannot_convert;
-        }
+        return convert_array_from_object(data, ct, init);
     }
     if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) {
         char *ptrdata;
         return 0;
     }
     if (ct->ct_flags & CT_PRIMITIVE_FLOAT) {
-        double value = PyFloat_AsDouble(init);
+        double value;
+        if ((ct->ct_flags & CT_IS_LONGDOUBLE) &&
+                CData_Check(init) &&
+                (((CDataObject *)init)->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+            long double lvalue;
+            lvalue = read_raw_longdouble_data(((CDataObject *)init)->c_data);
+            write_raw_longdouble_data(data, lvalue);
+            return 0;
+        }
+        value = PyFloat_AsDouble(init);
         if (value == -1.0 && PyErr_Occurred())
             return -1;
-        write_raw_float_data(data, value, ct->ct_size);
+        if (!(ct->ct_flags & CT_IS_LONGDOUBLE))
+            write_raw_float_data(data, value, ct->ct_size);
+        else
+            write_raw_longdouble_data(data, (long double)value);
         return 0;
     }
     if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
 
 static void cdata_dealloc(CDataObject *cd)
 {
+    if (cd->c_weakreflist != NULL)
+        PyObject_ClearWeakRefs((PyObject *) cd);
+
     Py_DECREF(cd->c_type);
     PyObject_Del(cd);
 }
 
-static void cdataowning_dealloc(CDataObject_own_base *cdb)
+static void cdataowning_dealloc(CDataObject *cd)
 {
-    if (cdb->weakreflist != NULL)
-        PyObject_ClearWeakRefs((PyObject *) cdb);
-
-    if (cdb->head.c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
-        Py_DECREF(((CDataObject_own_structptr *)cdb)->structobj);
+    if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
+        Py_DECREF(((CDataObject_own_structptr *)cd)->structobj);
     }
-    else if (cdb->head.c_type->ct_flags & CT_FUNCTIONPTR) {
+    else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) {
         /* a callback */
-        ffi_closure *closure = (ffi_closure *)cdb->head.c_data;
+        ffi_closure *closure = (ffi_closure *)cd->c_data;
         PyObject *args = (PyObject *)(closure->user_data);
         Py_XDECREF(args);
         cffi_closure_free(closure);
     }
-    cdata_dealloc(&cdb->head);
+    cdata_dealloc(cd);
 }
 
 static int cdata_traverse(CDataObject *cd, visitproc visit, void *arg)
     return 0;
 }
 
+static PyObject *cdata_float(CDataObject *cd);  /*forward*/
+
 static PyObject *cdata_repr(CDataObject *cd)
 {
-    char *p, *extra;
-    PyObject *result, *s = NULL;
+    char *extra;
+    PyObject *result, *s;
 
     if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) {
-        PyObject *o = convert_to_object(cd->c_data, cd->c_type);
-        if (o == NULL)
-            return NULL;
-        s = PyObject_Repr(o);
-        Py_DECREF(o);
-        if (s == NULL)
-            return NULL;
-        p = PyText_AsUTF8(s);
+        if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+            PyObject *o = convert_to_object(cd->c_data, cd->c_type);
+            if (o == NULL)
+                return NULL;
+            s = PyObject_Repr(o);
+            Py_DECREF(o);
+        }
+        else {
+            long double lvalue = read_raw_longdouble_data(cd->c_data);
+            char buffer[128];   /* big enough */
+            sprintf(buffer, "%LE", lvalue);
+            s = PyText_FromString(buffer);
+        }
     }
     else {
         if (cd->c_data != NULL) {
             s = PyText_FromFormat("%p", cd->c_data);
-            if (s == NULL)
-                return NULL;
-            p = PyText_AsUTF8(s);
         }
         else
-            p = "NULL";
+            s = PyText_FromString("NULL");
     }
+    if (s == NULL)
+        return NULL;
     /* it's slightly confusing to get "<cdata 'struct foo' 0x...>" because the
        struct foo is not owned.  Trying to make it clearer, write in this
        case "<cdata 'struct foo &' 0x...>". */
         extra = " &";
     else
         extra = "";
-    result = PyText_FromFormat("<cdata '%s%s' %s>",
-                               cd->c_type->ct_name, extra, p);
-    Py_XDECREF(s);
+    result = PyText_FromFormat("<cdata '%s%s' %S>",
+                               cd->c_type->ct_name, extra, s);
+    Py_DECREF(s);
     return result;
 }
 
-static PyObject *cdata_get_value(CDataObject *cd)
-{
-    if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR &&
-        cd->c_type->ct_size == sizeof(char)) {
-        return PyBytes_FromStringAndSize(cd->c_data, 1);
-    }
-    else if (cd->c_type->ct_itemdescr != NULL &&
-             cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR &&
-             cd->c_type->ct_itemdescr->ct_size == sizeof(char)) {
-        Py_ssize_t length;
-
-        if (cd->c_type->ct_flags & CT_ARRAY) {
-            const char *start = cd->c_data;
-            const char *end;
-            length = get_array_length(cd);
-            end = (const char *)memchr(start, 0, length);
-            if (end != NULL)
-                length = end - start;
-        }
-        else {
-            if (cd->c_data == NULL) {
-                PyObject *s = cdata_repr(cd);
-                if (s != NULL) {
-                    PyErr_Format(PyExc_RuntimeError,
-                                 "cannot use str() on %S", s);
-                    Py_DECREF(s);
-                }
-                return NULL;
-            }
-            length = strlen(cd->c_data);
-        }
-
-        return PyBytes_FromStringAndSize(cd->c_data, length);
-    }
-#ifdef HAVE_WCHAR_H
-    else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR &&
-        cd->c_type->ct_size == sizeof(wchar_t)) {
-        return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, 1);
-    }
-    else if (cd->c_type->ct_itemdescr != NULL &&
-             cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR &&
-             cd->c_type->ct_itemdescr->ct_size == sizeof(wchar_t)) {
-        Py_ssize_t length;
-        const wchar_t *start = (wchar_t *)cd->c_data;
-
-        if (cd->c_type->ct_flags & CT_ARRAY) {
-            const Py_ssize_t lenmax = get_array_length(cd);
-            length = 0;
-            while (length < lenmax && start[length])
-                length++;
-        }
-        else {
-            if (cd->c_data == NULL) {
-                PyObject *s = cdata_repr(cd);
-                if (s != NULL) {
-                    PyErr_Format(PyExc_RuntimeError,
-                                 "cannot use unicode() on %S", s);
-                    Py_DECREF(s);
-                }
-                return NULL;
-            }
-            length = 0;
-            while (start[length])
-                length++;
-        }
-
-        return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, length);
-    }
-#endif
-    else {
-        Py_INCREF(cd);
-        return (PyObject *)cd;
-    }
-}
-
-static PyObject *cdata_str(CDataObject *cd)
-{
-    if (cd->c_type->ct_flags & CT_IS_ENUM)
-        return convert_to_object(cd->c_data, cd->c_type);
-    else
-        return Py_TYPE(cd)->tp_repr((PyObject *)cd);
-}
-
 static PyObject *cdataowning_repr(CDataObject *cd)
 {
     Py_ssize_t size;
 #endif
     }
     else if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
-        PyObject *o = convert_to_object(cd->c_data, cd->c_type);
+        PyObject *o = cdata_float(cd);
 #if PY_MAJOR_VERSION < 3
         PyObject *r = o ? PyNumber_Int(o) : NULL;
 #else
 static PyObject *cdata_float(CDataObject *cd)
 {
     if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
-        return convert_to_object(cd->c_data, cd->c_type);
+        double value;
+        if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+            value = read_raw_float_data(cd->c_data, cd->c_type->ct_size);
+        }
+        else {
+            value = (double)read_raw_longdouble_data(cd->c_data);
+        }
+        return PyFloat_FromDouble(value);
     }
     PyErr_Format(PyExc_TypeError, "float() not supported on cdata '%s'",
                  cd->c_type->ct_name);
         if (cf != NULL) {
             /* read the field 'cf' */
             char *data = cd->c_data + cf->cf_offset;
-            if (cf->cf_bitshift >= 0)
+            if (cf->cf_bitshift == BS_REGULAR)
+                return convert_to_object(data, cf->cf_type);
+            else if (cf->cf_bitshift == BS_EMPTY_ARRAY)
+                return new_simple_cdata(data,
+                    (CTypeDescrObject *)cf->cf_type->ct_stuff);
+            else
                 return convert_to_object_bitfield(data, cf);
-            else
-                return convert_to_object(data, cf->cf_type);
         }
     }
     return PyObject_GenericGetAttr((PyObject *)cd, attr);
     return ct_int;
 }
 
+static PyObject *
+_prepare_pointer_call_argument(CTypeDescrObject *ctptr, PyObject *init)
+{
+    /* 'ctptr' is here a pointer type 'ITEM *'.  Accept as argument an
+       initializer for an array 'ITEM[]'.  This includes the case of
+       passing a Python string to a 'char *' argument. */
+    Py_ssize_t length, datasize;
+    CTypeDescrObject *ctitem = ctptr->ct_itemdescr;
+    PyObject *result;
+    char *data;
+
+    /* XXX some code duplication, how to avoid it? */
+    if (PyString_Check(init)) {
+        /* from a string: just returning the string here is fine.
+           We assume that the C code won't modify the 'char *' data. */
+        if ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) &&
+                (ctitem->ct_size == sizeof(char))) {
+            Py_INCREF(init);
+            return init;
+        }
+        else
+            return Py_None;
+    }
+    else if (PyList_Check(init) || PyTuple_Check(init)) {
+        length = PySequence_Fast_GET_SIZE(init);
+    }
+    else if (PyUnicode_Check(init)) {
+        /* from a unicode, we add the null terminator */
+        length = _my_PyUnicode_SizeAsWideChar(init) + 1;
+    }
+    else {
+        /* refuse to receive just an integer (and interpret it
+           as the array size) */
+        return Py_None;
+    }
+
+    if (ctitem->ct_size <= 0)
+        return Py_None;
+    datasize = length * ctitem->ct_size;
+    if ((datasize / ctitem->ct_size) != length) {
+        PyErr_SetString(PyExc_OverflowError,
+                        "array size would overflow a Py_ssize_t");
+        return NULL;
+    }
+
+    result = PyString_FromStringAndSize(NULL, datasize);
+    if (result == NULL)
+        return NULL;
+
+    data = PyString_AS_STRING(result);
+    memset(data, 0, datasize);
+    if (convert_array_from_object(data, ctptr, init) < 0) {
+        Py_DECREF(result);
+        return NULL;
+    }
+    return result;
+}
+
 static PyObject*
 cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds)
 {
     char *buffer;
     void** buffer_array;
     cif_description_t *cif_descr;
-    Py_ssize_t i, nargs, nargs_declared;
-    PyObject *signature, *res, *fvarargs;
+    Py_ssize_t i, nargs, nargs_declared, free_me_until = 0;
+    PyObject *signature, *res = NULL, *fvarargs;
     CTypeDescrObject *fresult;
     char *resultdata;
     char *errormsg;
         /* regular case: this function does not take '...' arguments */
         if (nargs != nargs_declared) {
             errormsg = "'%s' expects %zd arguments, got %zd";
-            goto bad_number_of_arguments;
+          bad_number_of_arguments:
+            PyErr_Format(PyExc_TypeError, errormsg,
+                         cd->c_type->ct_name, nargs_declared, nargs);
+            goto error;
         }
     }
     else {
         else
             argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(fvarargs, i);
 
-        if ((argtype->ct_flags & CT_POINTER) &&
-            (argtype->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR)) {
-            if (argtype->ct_itemdescr->ct_size == sizeof(char)) {
-                if (PyText_Check(obj)) {
-                    /* special case: Python string -> cdata 'char *' */
-                    *(char **)data = PyText_AsUTF8(obj);
+        if (argtype->ct_flags & CT_POINTER) {
+            PyObject *string;
+            if (!CData_Check(obj)) {
+                string = _prepare_pointer_call_argument(argtype, obj);
+                if (string != Py_None) {
+                    if (string == NULL)
+                        goto error;
+                    ((char **)data)[0] = PyBytes_AS_STRING(string);
+                    ((char **)data)[1] = (char *)string;
+                    assert(i < nargs_declared); /* otherwise, obj is a CData */
+                    free_me_until = i + 1;
                     continue;
                 }
             }
-#ifdef HAVE_WCHAR_H
-            else {
-                if (PyUnicode_Check(obj)) {
-                    /* Python Unicode string -> cdata 'wchar_t *':
-                       not supported yet */
-                    PyErr_SetString(PyExc_NotImplementedError,
-                        "automatic unicode-to-'wchar_t *' conversion");
-                    goto error;
-                }
+            ((char **)data)[1] = NULL;
+        }
+        if (convert_from_object(data, argtype, obj) < 0) {
+            if (CData_Check(obj) && (argtype->ct_flags & CT_IS_PTR_TO_OWNED) &&
+                   argtype->ct_itemdescr == ((CDataObject *)obj)->c_type) {
+                /* special case to make the life of verifier.py easier:
+                   if the formal argument type is 'struct foo *' but
+                   we pass a 'struct foo', then get a pointer to it */
+                PyErr_Clear();
+                ((char **)data)[0] = ((CDataObject *)obj)->c_data;
+                continue;
             }
-#endif
+            goto error;
         }
-        if (convert_from_object(data, argtype, obj) < 0)
-            goto error;
     }
 
     resultdata = buffer + cif_descr->exchange_offset_arg[0];
 
+    Py_BEGIN_ALLOW_THREADS
     restore_errno();
     ffi_call(&cif_descr->cif, (void (*)(void))(cd->c_data),
              resultdata, buffer_array);
     save_errno();
+    Py_END_ALLOW_THREADS
 
     if (fresult->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED |
                              CT_PRIMITIVE_UNSIGNED)) {
     else {
         res = convert_to_object(resultdata, fresult);
     }
-    PyObject_Free(buffer);
- done:
+    /* fall-through */
+
+ error:
+    for (i=0; i<free_me_until; i++) {
+        CTypeDescrObject *argtype;
+        argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 2 + i);
+        if (argtype->ct_flags & CT_POINTER) {
+            char *data = buffer + cif_descr->exchange_offset_arg[1 + i];
+            PyObject *string_or_null = (PyObject *)(((char **)data)[1]);
+            Py_XDECREF(string_or_null);
+        }
+    }
+    if (buffer)
+        PyObject_Free(buffer);
     if (fvarargs != NULL) {
         Py_DECREF(fvarargs);
         if (cif_descr != NULL)  /* but only if fvarargs != NULL, if variadic */
             PyObject_Free(cif_descr);
     }
     return res;
-
- bad_number_of_arguments:
-    PyErr_Format(PyExc_TypeError, errormsg,
-                 cd->c_type->ct_name, nargs_declared, nargs);
- error:
-    if (buffer)
-        PyObject_Free(buffer);
-    res = NULL;
-    goto done;
 }
 
 static PyObject *cdata_iter(CDataObject *);
     (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/
 };
 
-static PyMethodDef CData_methods[] = {
-    {NULL,              NULL}           /* sentinel */
-};
-
-static PyGetSetDef CData_getset[] = {
-    {"value", (getter)cdata_get_value, NULL, NULL},
-    {0}
-};
-
 static PyTypeObject CData_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
     "_cffi_backend.CData",
     &CData_as_mapping,                          /* tp_as_mapping */
     (hashfunc)cdata_hash,                       /* tp_hash */
     (ternaryfunc)cdata_call,                    /* tp_call */
-    (reprfunc)cdata_str,                        /* tp_str */
+    0,                                          /* tp_str */
     (getattrofunc)cdata_getattro,               /* tp_getattro */
     (setattrofunc)cdata_setattro,               /* tp_setattro */
     0,                                          /* tp_as_buffer */
     (traverseproc)cdata_traverse,               /* tp_traverse */
     0,                                          /* tp_clear */
     cdata_richcompare,                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
+    offsetof(CDataObject, c_weakreflist),       /* tp_weaklistoffset */
     (getiterfunc)cdata_iter,                    /* tp_iter */
     0,                                          /* tp_iternext */
-    CData_methods,                              /* tp_methods */
-    0,                                          /* tp_members */
-    CData_getset,                               /* tp_getset */
 };
 
 static PyTypeObject CDataOwning_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
     "_cffi_backend.CDataOwn",
-    sizeof(CDataObject_own_base),
+    sizeof(CDataObject),
     0,
     (destructor)cdataowning_dealloc,            /* tp_dealloc */
     0,                                          /* tp_print */
     0,                                          /* tp_traverse */
     0,                                          /* tp_clear */
     0,                                          /* tp_richcompare */
-    offsetof(CDataObject_own_base, weakreflist),/* tp_weaklistoffset */
+    0,                                          /* tp_weaklistoffset */
     0,                                          /* tp_iter */
     0,                                          /* tp_iternext */
     0,                                          /* tp_methods */
 
 /************************************************************/
 
-static CDataObject_own_base *allocate_owning_object(Py_ssize_t size,
-                                                    CTypeDescrObject *ct)
+static CDataObject *allocate_owning_object(Py_ssize_t size,
+                                           CTypeDescrObject *ct)
 {
-    CDataObject_own_base *cdb;
-    cdb = (CDataObject_own_base *)PyObject_Malloc(size);
-    if (PyObject_Init((PyObject *)cdb, &CDataOwning_Type) == NULL)
+    CDataObject *cd;
+    cd = (CDataObject *)PyObject_Malloc(size);
+    if (PyObject_Init((PyObject *)cd, &CDataOwning_Type) == NULL)
         return NULL;
 
     Py_INCREF(ct);
-    cdb->head.c_type = ct;
-    cdb->weakreflist = NULL;
-    return cdb;
+    cd->c_type = ct;
+    cd->c_weakreflist = NULL;
+    return cd;
 }
 
 static PyObject *
 convert_struct_to_owning_object(char *data, CTypeDescrObject *ct)
 {
-    CDataObject_own_base *cdb;
+    CDataObject *cd;
     Py_ssize_t dataoffset = offsetof(CDataObject_own_nolength, alignment);
     Py_ssize_t datasize = ct->ct_size;
 
                         "return type is not a struct or is opaque");
         return NULL;
     }
-    cdb = allocate_owning_object(dataoffset + datasize, ct);
-    if (cdb == NULL)
+    cd = allocate_owning_object(dataoffset + datasize, ct);
+    if (cd == NULL)
         return NULL;
-    cdb->head.c_data = ((char *)cdb) + dataoffset;
-
-    memcpy(cdb->head.c_data, data, datasize);
-    return (PyObject *)cdb;
+    cd->c_data = ((char *)cd) + dataoffset;
+
+    memcpy(cd->c_data, data, datasize);
+    return (PyObject *)cd;
 }
 
 static PyObject *b_newp(PyObject *self, PyObject *args)
 {
     CTypeDescrObject *ct, *ctitem;
     CDataObject *cd;
-    CDataObject_own_base *cdb;
     PyObject *init = Py_None;
     Py_ssize_t dataoffset, datasize, explicitlength;
     if (!PyArg_ParseTuple(args, "O!|O:newp", &CTypeDescr_Type, &ct, &init))
            we build two objects instead of one, with the memory-owning
            one being really the struct (or union) and the returned one
            having a strong reference to it */
-        CDataObject_own_base *cdp;
-
-        cdb = allocate_owning_object(dataoffset + datasize, ct->ct_itemdescr);
-        if (cdb == NULL)
+        CDataObject *cds;
+
+        cds = allocate_owning_object(dataoffset + datasize, ct->ct_itemdescr);
+        if (cds == NULL)
             return NULL;
 
-        cdp = allocate_owning_object(sizeof(CDataObject_own_structptr), ct);
-        if (cdp == NULL) {
-            Py_DECREF(cdb);
+        cd = allocate_owning_object(sizeof(CDataObject_own_structptr), ct);
+        if (cd == NULL) {
+            Py_DECREF(cds);
             return NULL;
         }
-        /* store the only reference to cdb into cdp */
-        ((CDataObject_own_structptr *)cdp)->structobj = (PyObject *)cdb;
+        /* store the only reference to cds into cd */
+        ((CDataObject_own_structptr *)cd)->structobj = (PyObject *)cds;
         assert(explicitlength < 0);
 
-        cdb->head.c_data = cdp->head.c_data = ((char *)cdb) + dataoffset;
-        cd = &cdp->head;
+        cds->c_data = cd->c_data = ((char *)cds) + dataoffset;
     }
     else {
-        cdb = allocate_owning_object(dataoffset + datasize, ct);
-        if (cdb == NULL)
+        cd = allocate_owning_object(dataoffset + datasize, ct);
+        if (cd == NULL)
             return NULL;
 
-        cdb->head.c_data = ((char *)cdb) + dataoffset;
+        cd->c_data = ((char *)cd) + dataoffset;
         if (explicitlength >= 0)
-            ((CDataObject_own_length*)cdb)->length = explicitlength;
-        cd = &cdb->head;
+            ((CDataObject_own_length*)cd)->length = explicitlength;
     }
 
     memset(cd->c_data, 0, datasize);
     Py_INCREF(ct);
     cd->c_type = ct;
     cd->c_data = ((char*)cd) + dataoffset;
+    cd->c_weakreflist = NULL;
     return cd;
 }
 
             }
             value = (unsigned char)PyBytes_AS_STRING(io)[0];
         }
+        else if ((ct->ct_flags & CT_IS_LONGDOUBLE) &&
+                 CData_Check(io) &&
+                 (((CDataObject *)io)->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+            long double lvalue;
+            lvalue = read_raw_longdouble_data(((CDataObject *)io)->c_data);
+            cd = _new_casted_primitive(ct);
+            if (cd != NULL)
+                write_raw_longdouble_data(cd->c_data, lvalue);
+            return (PyObject *)cd;
+        }
         else {
             value = PyFloat_AsDouble(io);
         }
             return NULL;
 
         cd = _new_casted_primitive(ct);
-        if (cd != NULL)
-            write_raw_float_data(cd->c_data, value, ct->ct_size);
+        if (cd != NULL) {
+            if (!(ct->ct_flags & CT_IS_LONGDOUBLE))
+                write_raw_float_data(cd->c_data, value, ct->ct_size);
+            else
+                write_raw_longdouble_data(cd->c_data, (long double)value);
+        }
         return (PyObject *)cd;
     }
     else {
 
 static PyObject *b_load_library(PyObject *self, PyObject *args)
 {
-    char *filename;
+    char *filename_or_null, *printable_filename;
     void *handle;
     DynLibObject *dlobj;
     int is_global = 0;
 
-    if (!PyArg_ParseTuple(args, "et|i:load_library",
-                          Py_FileSystemDefaultEncoding, &filename,
+    if (PyTuple_GET_SIZE(args) == 0 || PyTuple_GET_ITEM(args, 0) == Py_None) {
+        filename_or_null = NULL;
+        is_global = 1;
+    }
+    else if (!PyArg_ParseTuple(args, "et|i:load_library",
+                          Py_FileSystemDefaultEncoding, &filename_or_null,
                           &is_global))
         return NULL;
 
-    handle = dlopen(filename, RTLD_LAZY | (is_global?RTLD_GLOBAL:RTLD_LOCAL));
+    printable_filename = filename_or_null ? filename_or_null : "<None>";
+    handle = dlopen(filename_or_null,
+                    RTLD_LAZY | (is_global?RTLD_GLOBAL:RTLD_LOCAL));
     if (handle == NULL) {
-        PyErr_Format(PyExc_OSError, "cannot load library: %s", filename);
+        PyErr_Format(PyExc_OSError, "cannot load library: %s",
+                     printable_filename);
         return NULL;
     }
 
         return NULL;
     }
     dlobj->dl_handle = handle;
-    dlobj->dl_name = strdup(filename);
+    dlobj->dl_name = strdup(printable_filename);
     return (PyObject *)dlobj;
 }
 
        EPTYPE(ul, unsigned long, CT_PRIMITIVE_UNSIGNED )        \
        EPTYPE(ull, unsigned long long, CT_PRIMITIVE_UNSIGNED )  \
        EPTYPE(f, float, CT_PRIMITIVE_FLOAT )                    \
-       EPTYPE(d, double, CT_PRIMITIVE_FLOAT )
+       EPTYPE(d, double, CT_PRIMITIVE_FLOAT )                   \
+       EPTYPE(ld, long double, CT_PRIMITIVE_FLOAT | CT_IS_LONGDOUBLE )
 #ifdef HAVE_WCHAR_H
 # define ENUM_PRIMITIVE_TYPES_WCHAR                             \
        EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR )
             ffitype = &ffi_type_float;
         else if (strcmp(ptypes->name, "double") == 0)
             ffitype = &ffi_type_double;
+        else if (strcmp(ptypes->name, "long double") == 0)
+            ffitype = &ffi_type_longdouble;
         else
             goto bad_ffi_type;
     }
         return NULL;
 
     td->ct_size = sizeof(void *);
+    td->ct_length = -1;
     td->ct_flags = CT_POINTER;
     if (ctitem->ct_flags & (CT_STRUCT|CT_UNION))
         td->ct_flags |= CT_IS_PTR_TO_OWNED;
         if (fbitsize < 0 || (fbitsize == 8 * ftype->ct_size &&
                              !(ftype->ct_flags & CT_PRIMITIVE_CHAR))) {
             fbitsize = -1;
-            bitshift = -1;
+            if (ftype->ct_flags & CT_ARRAY && ftype->ct_length == 0)
+                bitshift = BS_EMPTY_ARRAY;
+            else
+                bitshift = BS_REGULAR;
             prev_bit_position = 0;
         }
         else {
             exchange_offset = ALIGN_ARG(exchange_offset);
             cif_descr->exchange_offset_arg[1 + i] = exchange_offset;
             exchange_offset += atype->size;
+            /* if 'farg' is a pointer type 'ITEM *', then we might receive
+               as argument to the function call what is an initializer
+               for an array 'ITEM[]'.  This includes the case of passing a
+               Python string to a 'char *' argument.  In this case, we
+               convert the initializer to a cdata 'ITEM[]' that gets
+               temporarily stored here: */
+            if (farg->ct_flags & CT_POINTER) {
+                exchange_offset += sizeof(PyObject *);
+            }
         }
     }
 
 {
     save_errno();
     {
+#ifdef WITH_THREAD
+    PyGILState_STATE state = PyGILState_Ensure();
+#endif
     PyObject *cb_args = (PyObject *)userdata;
     CTypeDescrObject *ct = (CTypeDescrObject *)PyTuple_GET_ITEM(cb_args, 0);
     PyObject *signature = ct->ct_stuff;
     Py_XDECREF(py_args);
     Py_XDECREF(py_res);
     Py_DECREF(cb_args);
+#ifdef WITH_THREAD
+    PyGILState_Release(state);
+#endif
     restore_errno();
     return;
 
 static PyObject *b_callback(PyObject *self, PyObject *args)
 {
     CTypeDescrObject *ct, *ctresult;
-    CDataObject_own_base *cdb;
+    CDataObject *cd;
     PyObject *ob, *error_ob = Py_None;
     PyObject *py_rawerr, *infotuple = NULL;
     cif_description_t *cif_descr;
 
     closure = cffi_closure_alloc();
 
-    cdb = PyObject_New(CDataObject_own_base, &CDataOwning_Type);
-    if (cdb == NULL)
+    cd = PyObject_New(CDataObject, &CDataOwning_Type);
+    if (cd == NULL)
         goto error;
     Py_INCREF(ct);
-    cdb->head.c_type = ct;
-    cdb->head.c_data = (char *)closure;
-    cdb->weakreflist = NULL;
+    cd->c_type = ct;
+    cd->c_data = (char *)closure;
+    cd->c_weakreflist = NULL;
 
     cif_descr = (cif_description_t *)ct->ct_extra;
     if (cif_descr == NULL) {
         goto error;
     }
     assert(closure->user_data == infotuple);
-    return (PyObject *)cdb;
+    return (PyObject *)cd;
 
  error:
     closure->user_data = NULL;
-    if (cdb == NULL)
+    if (cd == NULL)
         cffi_closure_free(closure);
     else
-        Py_DECREF(cdb);
+        Py_DECREF(cd);
     Py_XDECREF(infotuple);
     return NULL;
 }
     return PyText_FromStringAndSize(s, namelen + replacelen);
 }
 
+static PyObject *b_string(PyObject *self, PyObject *args)
+{
+    CDataObject *cd;
+    Py_ssize_t maxlen = -1;
+    if (!PyArg_ParseTuple(args, "O!|n:string",
+                          &CData_Type, &cd, &maxlen))
+        return NULL;
+
+    if (cd->c_type->ct_itemdescr != NULL &&
+        cd->c_type->ct_itemdescr->ct_flags & (CT_PRIMITIVE_CHAR |
+                                              CT_PRIMITIVE_SIGNED |
+                                              CT_PRIMITIVE_UNSIGNED)) {
+        Py_ssize_t length = maxlen;
+        if (cd->c_data == NULL) {
+            PyObject *s = cdata_repr(cd);
+            if (s != NULL) {
+                PyErr_Format(PyExc_RuntimeError,
+                             "cannot use string() on %s",
+                             PyString_AS_STRING(s));
+                Py_DECREF(s);
+            }
+            return NULL;
+        }
+        if (length < 0 && cd->c_type->ct_flags & CT_ARRAY) {
+            length = get_array_length(cd);
+        }
+        if (cd->c_type->ct_itemdescr->ct_size == sizeof(char)) {
+            const char *start = cd->c_data;
+            if (length < 0)
+                length = strlen(start);
+            else {
+                const char *end;
+                end = (const char *)memchr(start, 0, length);
+                if (end != NULL)
+                    length = end - start;
+            }
+            return PyString_FromStringAndSize(start, length);
+        }
+#ifdef HAVE_WCHAR_H
+        else if (cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR) {
+            const wchar_t *start = (wchar_t *)cd->c_data;
+            assert(cd->c_type->ct_itemdescr->ct_size == sizeof(wchar_t));
+            if (length < 0) {
+                length = 0;
+                while (start[length])
+                    length++;
+            }
+            else {
+                maxlen = length;
+                length = 0;
+                while (length < maxlen && start[length])
+                    length++;
+            }
+            return _my_PyUnicode_FromWideChar(start, length);
+        }
+#endif
+    }
+    else if (cd->c_type->ct_flags & CT_IS_ENUM) {
+        return convert_to_object(cd->c_data, cd->c_type);
+    }
+    else if (cd->c_type->ct_flags & (CT_PRIMITIVE_CHAR |
+                                     CT_PRIMITIVE_SIGNED |
+                                     CT_PRIMITIVE_UNSIGNED)) {
+        if (cd->c_type->ct_size == sizeof(char)) {
+            return PyString_FromStringAndSize(cd->c_data, 1);
+        }
+#ifdef HAVE_WCHAR_H
+        else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) {
+            assert(cd->c_type->ct_size == sizeof(wchar_t));
+            return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, 1);
+        }
+#endif
+    }
+    PyErr_Format(PyExc_TypeError, "string(): unexpected cdata '%s' argument",
+                 cd->c_type->ct_name);
+    return NULL;
+}
+
 static PyObject *b_buffer(PyObject *self, PyObject *args)
 {
     CDataObject *cd;
     return result;
 }
 
+static int _testfunc18(struct _testfunc17_s *ptr)
+{
+    return ptr->a1 + (int)ptr->a2;
+}
+
+static long double _testfunc19(long double x)
+{
+    int i;
+    for (i=0; i<28; i++)
+        x += x;
+    return x;
+}
+
+static short _testfunc20(struct _testfunc7_s *ptr)
+{
+    return ptr->a1 + ptr->a2;
+}
+
 static PyObject *b__testfunc(PyObject *self, PyObject *args)
 {
     /* for testing only */
     case 15: f = &_testfunc15; break;
     case 16: f = &_testfunc16; break;
     case 17: f = &_testfunc17; break;
+    case 18: f = &_testfunc18; break;
+    case 19: f = &_testfunc19; break;
+    case 20: f = &_testfunc20; break;
     default:
         PyErr_SetNone(PyExc_ValueError);
         return NULL;
     {"typeof", b_typeof, METH_O},
     {"offsetof", b_offsetof, METH_VARARGS},
     {"getcname", b_getcname, METH_VARARGS},
+    {"string", b_string, METH_VARARGS},
     {"buffer", b_buffer, METH_VARARGS},
     {"get_errno", b_get_errno, METH_NOARGS},
     {"set_errno", b_set_errno, METH_VARARGS},
 };
 #define INITERROR return NULL
 
-PyObject *
+PyMODINIT_FUNC
 PyInit__cffi_backend(void)
 #else
 #define INITERROR return
 
-void init_cffi_backend(void)
+PyMODINIT_FUNC
+init_cffi_backend(void)
 #endif
 {
     PyObject *m, *v;
     if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0)
         INITERROR;
 
-    v = PyText_FromString("0.2.1");
+    v = PyText_FromString("0.3");
     if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0)
         INITERROR;
 
     LPVOID p = TlsGetValue(cffi_tls_index);
 
     if (p == NULL) {
-        p = PyMem_Malloc(sizeof(struct cffi_errno_s));
+        /* XXX this malloc() leaks */
+        p = malloc(sizeof(struct cffi_errno_s));
         if (p == NULL)
             return NULL;
         memset(p, 0, sizeof(struct cffi_errno_s));
 
 def find_and_load_library(name, is_global=0):
     import ctypes.util
-    path = ctypes.util.find_library(name)
+    if name is None:
+        path = None
+    else:
+        path = ctypes.util.find_library(name)
     return load_library(path, is_global)
 
 def test_load_library():
 def test_no_float_on_int_types():
     p = new_primitive_type('long')
     py.test.raises(TypeError, float, cast(p, 42))
+    py.test.raises(TypeError, complex, cast(p, 42))
 
 def test_float_types():
     INF = 1E200 * 1E200
         assert float(cast(p, True)) == 1.0
         py.test.raises(TypeError, cast, p, None)
 
+def test_complex_types():
+    py.test.skip("later")
+    INF = 1E200 * 1E200
+    for name in ["float", "double"]:
+        p = new_primitive_type("_Complex " + name)
+        assert bool(cast(p, 0))
+        assert bool(cast(p, INF))
+        assert bool(cast(p, -INF))
+        assert bool(cast(p, 0j))
+        assert bool(cast(p, INF*1j))
+        assert bool(cast(p, -INF*1j))
+        py.test.raises(TypeError, int, cast(p, -150))
+        py.test.raises(TypeError, long, cast(p, -150))
+        py.test.raises(TypeError, float, cast(p, -150))
+        assert complex(cast(p, 1.25)) == 1.25
+        assert complex(cast(p, 1.25j)) == 1.25j
+        assert float(cast(p, INF*1j)) == INF*1j
+        assert float(cast(p, -INF)) == -INF
+        if name == "float":
+            assert complex(cast(p, 1.1j)) != 1.1j         # rounding error
+            assert complex(cast(p, 1E200+3j)) == INF+3j   # limited range
+            assert complex(cast(p, 3+1E200j)) == 3+INF*1j # limited range
+
+        assert cast(p, -1.1j) != cast(p, -1.1j)
+        assert repr(complex(cast(p, -0.0)).real) == '-0.0'
+        assert repr(complex(cast(p, -0j))) == '-0j'
+        assert complex(cast(p, '\x09')) == 9.0
+        assert complex(cast(p, True)) == 1.0
+        py.test.raises(TypeError, cast, p, None)
+        #
+        py.test.raises(cast, new_primitive_type(name), 1+2j)
+    py.test.raises(cast, new_primitive_type("int"), 1+2j)
+
 def test_character_type():
     p = new_primitive_type("char")
     assert bool(cast(p, '\x00'))
     assert long(cast(p, 'A')) == 65L
     assert type(int(cast(p, 'A'))) is int
     assert type(long(cast(p, 'A'))) is long
-    assert str(cast(p, 'A')) == 'A'
+    assert str(cast(p, 'A')) == repr(cast(p, 'A'))
     assert repr(cast(p, 'A')) == "<cdata 'char' 'A'>"
     assert repr(cast(p, 255)) == r"<cdata 'char' '\xff'>"
     assert repr(cast(p, 0)) == r"<cdata 'char' '\x00'>"
     assert p[0] == 'A'
     py.test.raises(TypeError, newp, BPtr, 65)
     py.test.raises(TypeError, newp, BPtr, "foo")
-    assert str(cast(BChar, 'A')) == 'A'
+    c = cast(BChar, 'A')
+    assert str(c) == repr(c)
+    assert int(c) == ord('A')
     py.test.raises(TypeError, cast, BChar, 'foo')
 
 def test_reading_pointer_to_pointer():
     p = newp(BIntPtrPtr, q)
     assert p[0][0] == 43
 
+def test_load_standard_library():
+    if sys.platform == "win32":
+        py.test.raises(OSError, find_and_load_library, None)
+        return
+    x = find_and_load_library(None)
+    BVoidP = new_pointer_type(new_void_type())
+    assert x.load_function(BVoidP, 'strcpy')
+    py.test.raises(KeyError, x.load_function,
+                   BVoidP, 'xxx_this_function_does_not_exist')
+
 def test_hash_differences():
     BChar = new_primitive_type("char")
     BInt = new_primitive_type("int")
     py.test.raises(TypeError, "p[0]")
 
 def test_default_str():
+    BChar = new_primitive_type("char")
+    x = cast(BChar, 42)
+    assert str(x) == repr(x)
     BInt = new_primitive_type("int")
     x = cast(BInt, 42)
     assert str(x) == repr(x)
     y = cast(BInt, x)
     assert int(y) == 42
     y = cast(new_primitive_type("char"), x)
-    assert str(y) == chr(42)
+    assert int(y) == 42
     y = cast(new_primitive_type("float"), x)
     assert float(y) == 42.0
     #
     #
     p = new_primitive_type("char")
     n = cast(p, cast(p, "A"))
-    assert str(n) == "A"
+    assert int(n) == ord("A")
 
 def test_new_primitive_from_cdata():
     p = new_primitive_type("int")
     BFunc6bis = new_function_type((BIntArray,), BIntPtr, False)
     f = cast(BFunc6bis, _testfunc(6))
     #
-    py.test.raises(TypeError, f, [142])
+    res = f([142])
+    assert typeof(res) is BIntPtr
+    assert res[0] == 142 - 1000
+    #
+    res = f((143,))
+    assert typeof(res) is BIntPtr
+    assert res[0] == 143 - 1000
     #
     x = newp(BIntArray, [242])
     res = f(x)
     assert typeof(res) is BIntPtr
     assert res[0] == 242 - 1000
+    #
+    py.test.raises(TypeError, f, 123456)
+    py.test.raises(TypeError, f, "foo")
+    py.test.raises(TypeError, f, u"bar")
 
 def test_call_function_7():
     BChar = new_primitive_type("char")
     res = f(x[0])
     assert res == -4042 + ord('A')
 
+def test_call_function_20():
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BStruct = new_struct_type("foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BShort, -1)])
+    BFunc18 = new_function_type((BStructPtr,), BShort, False)
+    f = cast(BFunc18, _testfunc(20))
+    x = newp(BStructPtr, {'a1': 'A', 'a2': -4042})
+    # test the exception that allows us to pass a 'struct foo' where the
+    # function really expects a 'struct foo *'.
+    res = f(x[0])
+    assert res == -4042 + ord('A')
+    assert res == f(x)
+
 def test_call_function_9():
     BInt = new_primitive_type("int")
     BFunc9 = new_function_type((BInt,), BInt, True)    # vararg
     BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
     e = cast(BEnum, 0)
     assert repr(e) == "<cdata 'enum foo' 'def'>"
-    assert str(e) == 'def'
-    assert str(cast(BEnum, -20)) == 'ab'
-    assert str(cast(BEnum, 'c')) == 'c'
+    assert string(e) == 'def'
+    assert string(cast(BEnum, -20)) == 'ab'
+    assert string(cast(BEnum, 'c')) == 'c'
     assert int(cast(BEnum, 'c')) == 1
     assert int(cast(BEnum, 'def')) == 0
     assert int(cast(BEnum, -242 + 2**128)) == -242
-    assert str(cast(BEnum, -242 + 2**128)) == '#-242'
-    assert str(cast(BEnum, '#-20')) == 'ab'
+    assert string(cast(BEnum, -242 + 2**128)) == '#-242'
+    assert string(cast(BEnum, '#-20')) == 'ab'
     assert repr(cast(BEnum, '#-20')) == "<cdata 'enum foo' 'ab'>"
     assert repr(cast(BEnum, '#-21')) == "<cdata 'enum foo' '#-21'>"
 
     BPtr = new_pointer_type(BInt)
     weakref.ref(BInt)
     weakref.ref(newp(BPtr, 42))
-    py.test.raises(TypeError, weakref.ref, cast(BPtr, 42))
-    py.test.raises(TypeError, weakref.ref, cast(BInt, 42))
+    weakref.ref(cast(BPtr, 42))
+    weakref.ref(cast(BInt, 42))
 
 def test_no_inheritance():
     BInt = new_primitive_type("int")
     BArray1 = new_array_type(new_pointer_type(BChar), 5)
     BArray2 = new_array_type(new_pointer_type(BArray1), 5)
     a = newp(BArray2, ["abc", "de", "ghij"])
-    assert str(a[2]) == "ghij"
+    assert string(a[1]) == "de"
+    assert string(a[2]) == "ghij"
     a[2] = "."
-    assert str(a[2]) == "."
+    assert string(a[2]) == "."
     a[2] = "12345"
-    assert str(a[2]) == "12345"
+    assert string(a[2]) == "12345"
     e = py.test.raises(IndexError, 'a[2] = "123456"')
     assert 'char[5]' in str(e.value)
     assert 'got 6 characters' in str(e.value)
     p2 = newp(new_pointer_type(BFunc), p1)
     assert p2[0] == p1
 
-def test_str():
+def test_string():
     BChar = new_primitive_type("char")
+    assert string(cast(BChar, 42)) == '*'
+    assert string(cast(BChar, 0)) == '\x00'
     BCharP = new_pointer_type(BChar)
     BArray = new_array_type(BCharP, 10)
     a = newp(BArray, "hello")
     assert len(a) == 10
-    assert str(a) == "hello"
+    assert string(a) == "hello"
     p = a + 2
-    assert str(p) == "llo"
-    py.test.raises(RuntimeError, str, cast(BCharP, 0))
+    assert string(p) == "llo"
+    assert string(newp(new_array_type(BCharP, 4), "abcd")) == "abcd"
+    py.test.raises(RuntimeError, string, cast(BCharP, 0))
+    assert string(a, 4) == "hell"
+    assert string(a, 5) == "hello"
+    assert string(a, 6) == "hello"
+
+def test_string_byte():
+    BByte = new_primitive_type("signed char")
+    assert string(cast(BByte, 42)) == '*'
+    assert string(cast(BByte, 0)) == '\x00'
+    BArray = new_array_type(new_pointer_type(BByte), None)
+    a = newp(BArray, [65, 66, 67])
+    assert type(string(a)) is str and string(a) == 'ABC'
+    #
+    BByte = new_primitive_type("unsigned char")
+    assert string(cast(BByte, 42)) == '*'
+    assert string(cast(BByte, 0)) == '\x00'
+    BArray = new_array_type(new_pointer_type(BByte), None)
+    a = newp(BArray, [65, 66, 67])
+    assert type(string(a)) is str and string(a) == 'ABC'
+    if 'PY_DOT_PY' not in globals():
+        assert string(a, 8).startswith('ABC')  # may contain additional garbage
+
+def test_string_wchar():
+    BWChar = new_primitive_type("wchar_t")
+    assert string(cast(BWChar, 42)) == u'*'
+    assert string(cast(BWChar, 0x4253)) == u'\u4253'
+    assert string(cast(BWChar, 0)) == u'\x00'
+    BArray = new_array_type(new_pointer_type(BWChar), None)
+    a = newp(BArray, [u'A', u'B', u'C'])
+    assert type(string(a)) is unicode and string(a) == u'ABC'
+    if 'PY_DOT_PY' not in globals():
+        assert string(a, 8).startswith(u'ABC') # may contain additional garbage
+
+def test_string_typeerror():
+    BShort = new_primitive_type("short")
+    BArray = new_array_type(new_pointer_type(BShort), None)
+    a = newp(BArray, [65, 66, 67])
+    py.test.raises(TypeError, string, a)
 
 def test_bug_convert_to_ptr():
     BChar = new_primitive_type("char")
     BStructPtr = new_pointer_type(BStruct)
     complete_struct_or_union(BStruct, [('a1', BCharArray10, -1)])
     p = newp(BStructPtr, None)
-    assert str(p.a1) == ''
+    assert string(p.a1) == ''
     p.a1 = 'foo'
-    assert str(p.a1) == 'foo'
+    assert string(p.a1) == 'foo'
     assert list(p.a1) == ['f', 'o', 'o'] + ['\x00'] * 7
     p.a1 = ['x', 'y']
-    assert str(p.a1) == 'xyo'
+    assert string(p.a1) == 'xyo'
 
 def test_invalid_function_result_types():
     BFunc = new_function_type((), new_void_type())
     assert repr(s) == "<cdata 'struct test17' owning 8 bytes>"
     assert s.a1 == 40
     assert s.a2 == 40.0 * 40.0
+    #
+    BStruct17Ptr = new_pointer_type(BStruct17)
+    BFunc18 = new_function_type((BStruct17Ptr,), BInt)
+    f = cast(BFunc18, _testfunc(18))
+    x = f([[40, 2.5]])
+    assert x == 42
+    x = f([{'a2': 43.1}])
+    assert x == 43
 
 def test_cast_with_functionptr():
     BFunc = new_function_type((), new_void_type())
     if wchar4:
         x = cast(BWChar, 0x12345)
         assert str(x) == "<cdata 'wchar_t' u'\U00012345'>"
-        assert unicode(x) == u'\U00012345'
+        assert int(x) == 0x12345
     else:
         assert not pyuni4
     #
     BWCharArray = new_array_type(BWCharP, None)
     a = newp(BWCharArray, u'hello \u1234 world')
     assert len(a) == 14   # including the final null
-    assert unicode(a) == u'hello \u1234 world'
+    assert string(a) == u'hello \u1234 world'
     a[13] = u'!'
-    assert unicode(a) == u'hello \u1234 world!'
+    assert string(a) == u'hello \u1234 world!'
     assert str(a) == repr(a)
     assert a[6] == u'\u1234'
     a[6] = u'-'
-    assert unicode(a) == 'hello - world!'
+    assert string(a) == u'hello - world!'
     assert str(a) == repr(a)
     #
     if wchar4:
         u = u'\U00012345\U00012346\U00012347'
         a = newp(BWCharArray, u)
         assert len(a) == 4
-        assert unicode(a) == u
+        assert string(a) == u
         assert len(list(a)) == 4
         expected = [u'\U00012345', u'\U00012346', u'\U00012347', unichr(0)]
         assert list(a) == expected
     w = cast(BWChar, 'a')
     assert repr(w) == "<cdata 'wchar_t' u'a'>"
     assert str(w) == repr(w)
-    assert unicode(w) == u'a'
+    assert string(w) == u'a'
     assert int(w) == ord('a')
     w = cast(BWChar, 0x1234)
     assert repr(w) == "<cdata 'wchar_t' u'\u1234'>"
     assert str(w) == repr(w)
-    assert unicode(w) == u'\u1234'
+    assert string(w) == u'\u1234'
     assert int(w) == 0x1234
     w = cast(BWChar, u'\u8234')
     assert repr(w) == "<cdata 'wchar_t' u'\u8234'>"
     assert str(w) == repr(w)
-    assert unicode(w) == u'\u8234'
+    assert string(w) == u'\u8234'
     assert int(w) == 0x8234
     w = cast(BInt, u'\u1234')
     assert repr(w) == "<cdata 'int' 4660>"
         w = cast(BWChar, u'\U00012345')
         assert repr(w) == "<cdata 'wchar_t' u'\U00012345'>"
         assert str(w) == repr(w)
-        assert unicode(w) == u'\U00012345'
+        assert string(w) == u'\U00012345'
         assert int(w) == 0x12345
         w = cast(BInt, u'\U00012345')
         assert repr(w) == "<cdata 'int' 74565>"
     #
     a = newp(BWCharArray, u'hello - world')
     p = cast(BWCharP, a)
-    assert unicode(p) == u'hello - world'
+    assert string(p) == u'hello - world'
     p[6] = u'\u2345'
-    assert unicode(p) == u'hello \u2345 world'
+    assert string(p) == u'hello \u2345 world'
     #
     s = newp(BStructPtr, [u'\u1234', p])
     assert s.a1 == u'\u1234'
     assert s.a2 == p
     assert str(s.a2) == repr(s.a2)
-    assert unicode(s.a2) == u'hello \u2345 world'
+    assert string(s.a2) == u'hello \u2345 world'
     #
     q = cast(BWCharP, 0)
     assert str(q) == repr(q)
-    py.test.raises(RuntimeError, unicode, q)
+    py.test.raises(RuntimeError, string, q)
     #
     def cb(p):
         assert repr(p).startswith("<cdata 'wchar_t *' 0x")
-        return len(unicode(p))
+        return len(string(p))
     BFunc = new_function_type((BWCharP,), BInt, False)
     f = callback(BFunc, cb, -42)
-    #assert f(u'a\u1234b') == 3    -- not implemented
-    py.test.raises(NotImplementedError, f, u'a\u1234b')
+    assert f(u'a\u1234b') == 3
     #
     if wchar4 and not pyuni4:
         # try out-of-range wchar_t values
         x = cast(BWChar, 1114112)
-        py.test.raises(ValueError, unicode, x)
+        py.test.raises(ValueError, string, x)
         x = cast(BWChar, -1)
-        py.test.raises(ValueError, unicode, x)
+        py.test.raises(ValueError, string, x)
 
 def test_keepalive_struct():
     # exception to the no-keepalive rule: p=newp(BStructPtr) returns a
     assert c[2] == '-'
     assert str(buf) == "hi-there\x00"
     buf[:2] = 'HI'
-    assert str(c) == 'HI-there'
+    assert string(c) == 'HI-there'
     assert buf[:4:2] == 'H-'
     if '__pypy__' not in sys.builtin_module_names:
         # XXX pypy doesn't support the following assignment so far
         buf[:4:2] = 'XY'
-        assert str(c) == 'XIYthere'
+        assert string(c) == 'XIYthere'
 
 def test_getcname():
     BUChar = new_primitive_type("unsigned char")
     assert x.a1 == 0
     assert len(x.a2) == 2
     assert list(x.a2) == [4, 5]
+
+def test_autocast_int():
+    BInt = new_primitive_type("int")
+    BIntPtr = new_pointer_type(BInt)
+    BLongLong = new_primitive_type("long long")
+    BULongLong = new_primitive_type("unsigned long long")
+    BULongLongPtr = new_pointer_type(BULongLong)
+    x = newp(BIntPtr, cast(BInt, 42))
+    assert x[0] == 42
+    x = newp(BIntPtr, cast(BLongLong, 42))
+    assert x[0] == 42
+    x = newp(BIntPtr, cast(BULongLong, 42))
+    assert x[0] == 42
+    x = newp(BULongLongPtr, cast(BInt, 42))
+    assert x[0] == 42
+    py.test.raises(OverflowError, newp, BULongLongPtr, cast(BInt, -42))
+    x = cast(BInt, cast(BInt, 42))
+    assert int(x) == 42
+    x = cast(BInt, cast(BLongLong, 42))
+    assert int(x) == 42
+    x = cast(BInt, cast(BULongLong, 42))
+    assert int(x) == 42
+    x = cast(BULongLong, cast(BInt, 42))
+    assert int(x) == 42
+    x = cast(BULongLong, cast(BInt, -42))
+    assert int(x) == 2 ** 64 - 42
+    x = cast(BIntPtr, cast(BInt, 42))
+    assert int(cast(BInt, x)) == 42
+
+def test_autocast_float():
+    BFloat = new_primitive_type("float")
+    BDouble = new_primitive_type("float")
+    BFloatPtr = new_pointer_type(BFloat)
+    x = newp(BFloatPtr, cast(BDouble, 12.5))
+    assert x[0] == 12.5
+    x = cast(BFloat, cast(BDouble, 12.5))
+    assert float(x) == 12.5
+
+def test_longdouble():
+    py_py = 'PY_DOT_PY' in globals()
+    BLongDouble = new_primitive_type("long double")
+    BLongDoublePtr = new_pointer_type(BLongDouble)
+    BLongDoubleArray = new_array_type(BLongDoublePtr, None)
+    a = newp(BLongDoubleArray, 1)
+    x = a[0]
+    if not py_py:
+        assert repr(x).startswith("<cdata 'long double' 0.0")
+    assert float(x) == 0.0
+    assert int(x) == 0
+    #
+    b = newp(BLongDoubleArray, [1.23])
+    x = b[0]
+    if not py_py:
+        assert repr(x).startswith("<cdata 'long double' 1.23")
+    assert float(x) == 1.23
+    assert int(x) == 1
+    #
+    BFunc19 = new_function_type((BLongDouble,), BLongDouble)
+    f = cast(BFunc19, _testfunc(19))
+    start = 8
+    for i in range(107):
+        start = f(start)
+    if sizeof(BLongDouble) > sizeof(new_primitive_type("double")):
+        if not py_py:
+            assert repr(start).startswith("<cdata 'long double' 6.15")
+            assert repr(start).endswith("E+902>")
+        #
+        c = newp(BLongDoubleArray, [start])
+        x = c[0]
+        if not py_py:
+            assert repr(x).endswith("E+902>")
+            assert float(x) == float("inf")
+
+def test_get_array_of_length_zero():
+    for length in [0, 5, 10]:
+        BLong = new_primitive_type("long")
+        BLongP = new_pointer_type(BLong)
+        BArray0 = new_array_type(BLongP, length)
+        BStruct = new_struct_type("foo")
+        BStructPtr = new_pointer_type(BStruct)
+        complete_struct_or_union(BStruct, [('a1', BArray0, -1)])
+        p = newp(BStructPtr, None)
+        if length == 0:
+            assert repr(p.a1).startswith("<cdata 'long *' 0x")
+        else:
+            assert repr(p.a1).startswith("<cdata 'long[%d]' 0x" % length)
 from .api import FFI, CDefError, FFIError
 from .ffiplatform import VerificationError, VerificationMissing
 
-__version__ = "0.2.1"
-__version_info__ = (0, 2, 1)
+__version__ = "0.3"
+__version_info__ = (0, 3)
             cdecl = self._typeof(cdecl)
         return self._backend.cast(cdecl, source)
 
+    def string(self, cdata, maxlen=-1):
+        """Return a Python string (or unicode string) from the 'cdata'.
+        If 'cdata' is a pointer or array of characters or bytes, returns
+        the null-terminated string.  The returned string extends until
+        the first null character, or at most 'maxlen' characters.  If
+        'cdata' is an array then 'maxlen' defaults to its length.
+
+        If 'cdata' is a pointer or array of wchar_t, returns a unicode
+        string following the same rules.
+
+        If 'cdata' is a single character or byte or a wchar_t, returns
+        it as a string or unicode string.
+
+        If 'cdata' is an enum, returns the value of the enumerator as a
+        string, or "#NUMBER" if the value is out of range.
+        """
+        return self._backend.string(cdata, maxlen)
+
     def buffer(self, cdata, size=-1):
         """Return a read-write buffer object that references the raw C data
         pointed to by the given 'cdata'.  The 'cdata' must be a pointer or
             replace_with = ' ' + replace_with
         return self._backend.getcname(cdecl, replace_with)
 
+    def gc(self, cdata, destructor):
+        """Return a new cdata object that points to the same
+        data.  Later, when this new cdata object is garbage-collected,
+        'destructor(old_cdata_object)' will be called.
+        """
+        try:
+            gc_weakrefs = self.gc_weakrefs
+        except AttributeError:
+            from .gc_weakref import GcWeakrefs
+            gc_weakrefs = self.gc_weakrefs = GcWeakrefs(self)
+        return gc_weakrefs.build(cdata, destructor)
+
     def _get_cached_btype(self, type):
         try:
             BType = self._cached_btypes[type]
     #
     backend = ffi._backend
     backendlib = backend.load_library(path)
-    function_cache = {}
+    #
+    def make_accessor(name):
+        key = 'function ' + name
+        if key in ffi._parser._declarations:
+            tp = ffi._parser._declarations[key]
+            BType = ffi._get_cached_btype(tp)
+            value = backendlib.load_function(BType, name)
+            library.__dict__[name] = value
+            return
+        #
+        key = 'variable ' + name
+        if key in ffi._parser._declarations:
+            tp = ffi._parser._declarations[key]
+            BType = ffi._get_cached_btype(tp)
+            read_variable = backendlib.read_variable
+            write_variable = backendlib.write_variable
+            setattr(FFILibrary, name, property(
+                lambda self: read_variable(BType, name),
+                lambda self, value: write_variable(BType, name, value)))
+            return
+        #
+        raise AttributeError(name)
     #
     class FFILibrary(object):
-        def __getattribute__(self, name):
+        def __getattr__(self, name):
+            make_accessor(name)
+            return getattr(self, name)
+        def __setattr__(self, name, value):
             try:
-                return function_cache[name]
-            except KeyError:
-                pass
-            #
-            key = 'function ' + name
-            if key in ffi._parser._declarations:
-                tp = ffi._parser._declarations[key]
-                BType = ffi._get_cached_btype(tp)
-                value = backendlib.load_function(BType, name)
-                function_cache[name] = value
-                return value
-            #
-            key = 'variable ' + name
-            if key in ffi._parser._declarations:
-                tp = ffi._parser._declarations[key]
-                BType = ffi._get_cached_btype(tp)
-                return backendlib.read_variable(BType, name)
-            #
-            raise AttributeError(name)
-
-        def __setattr__(self, name, value):
-            key = 'variable ' + name
-            if key in ffi._parser._declarations:
-                tp = ffi._parser._declarations[key]
-                BType = ffi._get_cached_btype(tp)
-                backendlib.write_variable(BType, name, value)
-                return
-            #
-            raise AttributeError(name)
+                property = getattr(self.__class__, name)
+            except AttributeError:
+                make_accessor(name)
+                setattr(self, name, value)
+            else:
+                property.__set__(self, value)
     #
     if libname is not None:
         FFILibrary.__name__ = 'FFILibrary_%s' % libname
-    return FFILibrary(), function_cache
+    library = FFILibrary()
+    return library, library.__dict__

cffi/backend_ctypes.py

-import ctypes, ctypes.util, operator
+import ctypes, ctypes.util, operator, sys
 from . import model
 import sys
 
-if sys.version < '3':
+if sys.version_info < (3,):
     integer_types = (int, long)
     bytes = str
 else:
     xrange = range
 
 class CTypesData(object):
-    __slots__ = []
+    __slots__ = ['__weakref__']
 
     def __init__(self, *args):
         raise TypeError("cannot instantiate %r" % (self.__class__,))
     def __hash__(self):
         return hash(type(self)) ^ hash(self._convert_to_address(None))
 
+    def _to_string(self, maxlen):
+        raise TypeError("string(): %r" % (self,))
+
 
 class CTypesGenericPrimitive(CTypesData):
     __slots__ = []
         elif name in ('float', 'double'):
             kind = 'float'
         else:
-            kind = 'int'
+            if name in ('signed char', 'unsigned char'):
+                kind = 'byte'
+            else:
+                kind = 'int'
             is_signed = (ctype(-1).value == -1)
         #
         def _cast_source_to_int(source):
                     return ctype()
                 return ctype(CTypesPrimitive._to_ctypes(init))
 
-            if kind == 'int':
+            if kind == 'int' or kind == 'byte':
                 @classmethod
                 def _cast_from(cls, source):
                     source = _cast_source_to_int(source)
                     return cls(source)
                 def __int__(self):
                     return ord(self._value)
-                @property
-                def value(self):
-                    return self._value
 
             if kind == 'float':
                 @classmethod
 
             _cast_to_integer = __int__
 
-            if kind == 'int':
+            if kind == 'int' or kind == 'byte':
                 @staticmethod
                 def _to_ctypes(x):
                     if not isinstance(x, integer_types):
                         return x
                     if isinstance(x, CTypesPrimitive):    # <CData <char>>
                         return x._value
-                    if sys.version >= '3' and isinstance(x, int):
+                    if sys.version_info >= (3,) and isinstance(x, int):
                         return x
                     raise TypeError("character expected, got %s" %
                                     type(x).__name__)
             @staticmethod
             def _initialize(blob, init):
                 blob.value = CTypesPrimitive._to_ctypes(init)
+
+            if kind == 'char':
+                def _to_string(self, maxlen):
+                    return self._value
+            if kind == 'byte':
+                def _to_string(self, maxlen):
+                    return chr(self._value & 0xff)
         #
         CTypesPrimitive._fix_class()
         return CTypesPrimitive
 
     def new_pointer_type(self, BItem):
-        if BItem is self.ffi._get_cached_btype(model.PrimitiveType('char')):
+        getbtype = self.ffi._get_cached_btype
+        if BItem is getbtype(model.PrimitiveType('char')):
             kind = 'charp'
+        elif BItem in (getbtype(model.PrimitiveType('signed char')),
+                       getbtype(model.PrimitiveType('unsigned char'))):
+            kind = 'bytep'
         else:
             kind = 'generic'
         #
                 self._as_ctype_ptr[index] = BItem._to_ctypes(value)
 
             if kind == 'charp':
-                @property
-                def value(self):
-                    n = 0
-                    while self._as_ctype_ptr[n] != b'\x00':
-                        n += 1
-                    chars = [self._as_ctype_ptr[i] for i in range(n)]
-                    return b''.join(chars)
                 @classmethod
                 def _arg_to_ctypes(cls, value):
                     if isinstance(value, bytes):
                     else:
                         return super(CTypesPtr, cls)._arg_to_ctypes(value)
 
+            if kind == 'charp' or kind == 'bytep':
+                def _to_string(self, maxlen):
+                    if maxlen < 0:
+                        maxlen = sys.maxint
+                    p = ctypes.cast(self._as_ctype_ptr,
+                                    ctypes.POINTER(ctypes.c_char))
+                    n = 0
+                    while n < maxlen and p[n] != '\x00':
+                        n += 1
+                    return ''.join([p[i] for i in range(n)])
+
             def _get_own_repr(self):
                 if getattr(self, '_own', False):
                     return 'owning %d bytes' % (
         else:
             brackets = ' &[%d]' % length
         BItem = CTypesPtr._BItem
-        if BItem is self.ffi._get_cached_btype(model.PrimitiveType('char')):
+        getbtype = self.ffi._get_cached_btype
+        if BItem is getbtype(model.PrimitiveType('char')):
             kind = 'char'
+        elif BItem in (getbtype(model.PrimitiveType('signed char')),
+                       getbtype(model.PrimitiveType('unsigned char'))):
+            kind = 'byte'
         else:
             kind = 'generic'
         #
             else:
                 __slots__.append('_ctype')
             _reftypename = BItem._get_c_name(brackets)
+            _declared_length = length
+            _CTPtr = CTypesPtr
 
             def __init__(self, init):
                 if length is None:
                     raise IndexError
                 self._blob[index] = BItem._to_ctypes(value)