text: #include "Python.h"
  #include <ffi.h>
  #ifdef MS_WIN32
  #include <windows.h>
  #include <malloc.h>
  #endif
  #include "ctypes.h"
  
  /******************************************************************/
  /*
    StdDict - a dictionary subclass, containing additional C accessible fields
  
    XXX blabla more
  */
  
  /* Seems we need this, otherwise we get problems when calling
   * PyDict_SetItem() (ma_lookup is NULL)
   */
  static int
  PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds)
  {
      if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0)
          return -1;
      self->format = NULL;
      self->ndim = 0;
      self->shape = NULL;
      return 0;
  }
  
  static int
  PyCStgDict_clear(StgDictObject *self)
  {
      Py_CLEAR(self->proto);
      Py_CLEAR(self->argtypes);
      Py_CLEAR(self->converters);
      Py_CLEAR(self->restype);
      Py_CLEAR(self->checker);
      return 0;
  }
  
  static void
  PyCStgDict_dealloc(StgDictObject *self)
  {
      PyCStgDict_clear(self);
      PyMem_Free(self->format);
      PyMem_Free(self->shape);
      PyMem_Free(self->ffi_type_pointer.elements);
      PyDict_Type.tp_dealloc((PyObject *)self);
  }
  
  int
  PyCStgDict_clone(StgDictObject *dst, StgDictObject *src)
  {
      char *d, *s;
      Py_ssize_t size;
  
      PyCStgDict_clear(dst);
      PyMem_Free(dst->ffi_type_pointer.elements);
      PyMem_Free(dst->format);
      dst->format = NULL;
      PyMem_Free(dst->shape);
      dst->shape = NULL;
      dst->ffi_type_pointer.elements = NULL;
  
      d = (char *)dst;
      s = (char *)src;
      memcpy(d + sizeof(PyDictObject),
             s + sizeof(PyDictObject),
             sizeof(StgDictObject) - sizeof(PyDictObject));
  
      Py_XINCREF(dst->proto);
      Py_XINCREF(dst->argtypes);
      Py_XINCREF(dst->converters);
      Py_XINCREF(dst->restype);
      Py_XINCREF(dst->checker);
  
      if (src->format) {
          dst->format = PyMem_Malloc(strlen(src->format) + 1);
          if (dst->format == NULL) {
              PyErr_NoMemory();
              return -1;
          }
          strcpy(dst->format, src->format);
      }
      if (src->shape) {
          dst->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src->ndim);
          if (dst->shape == NULL) {
              PyErr_NoMemory();
              return -1;
          }
          memcpy(dst->shape, src->shape,
                 sizeof(Py_ssize_t) * src->ndim);
      }
  
      if (src->ffi_type_pointer.elements == NULL)
          return 0;
      size = sizeof(ffi_type *) * (src->length + 1);
      dst->ffi_type_pointer.elements = PyMem_Malloc(size);
      if (dst->ffi_type_pointer.elements == NULL) {
          PyErr_NoMemory();
          return -1;
      }
      memcpy(dst->ffi_type_pointer.elements,
             src->ffi_type_pointer.elements,
             size);
      return 0;
  }
  
  PyTypeObject PyCStgDict_Type = {
      PyVarObject_HEAD_INIT(NULL, 0)
      "StgDict",
      sizeof(StgDictObject),
      0,
      (destructor)PyCStgDict_dealloc,             /* tp_dealloc */
      0,                                          /* tp_print */
      0,                                          /* tp_getattr */
      0,                                          /* tp_setattr */
      0,                                          /* tp_reserved */
      0,                                          /* tp_repr */
      0,                                          /* tp_as_number */
      0,                                          /* tp_as_sequence */
      0,                                          /* tp_as_mapping */
      0,                                          /* tp_hash */
      0,                                          /* tp_call */
      0,                                          /* tp_str */
      0,                                          /* tp_getattro */
      0,                                          /* tp_setattro */
      0,                                          /* tp_as_buffer */
      Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
      0,                                          /* tp_doc */
      0,                                          /* tp_traverse */
      0,                                          /* tp_clear */
      0,                                          /* tp_richcompare */
      0,                                          /* tp_weaklistoffset */
      0,                                          /* tp_iter */
      0,                                          /* tp_iternext */
      0,                                          /* tp_methods */
      0,                                          /* tp_members */
      0,                                          /* tp_getset */
      0,                                          /* tp_base */
      0,                                          /* tp_dict */
      0,                                          /* tp_descr_get */
      0,                                          /* tp_descr_set */
      0,                                          /* tp_dictoffset */
      (initproc)PyCStgDict_init,                          /* tp_init */
      0,                                          /* tp_alloc */
      0,                                          /* tp_new */
      0,                                          /* tp_free */
  };
  
  /* May return NULL, but does not set an exception! */
  StgDictObject *
  PyType_stgdict(PyObject *obj)
  {
      PyTypeObject *type;
  
      if (!PyType_Check(obj))
          return NULL;
      type = (PyTypeObject *)obj;
      if (!type->tp_dict || !PyCStgDict_CheckExact(type->tp_dict))
          return NULL;
      return (StgDictObject *)type->tp_dict;
  }
  
  /* May return NULL, but does not set an exception! */
  /*
    This function should be as fast as possible, so we don't call PyType_stgdict
    above but inline the code, and avoid the PyType_Check().
  */
  StgDictObject *
  PyObject_stgdict(PyObject *self)
  {
      PyTypeObject *type = Py_TYPE(self);
      if (!type->tp_dict || !PyCStgDict_CheckExact(type->tp_dict))
          return NULL;
      return (StgDictObject *)type->tp_dict;
  }
  
  /* descr is the descriptor for a field marked as anonymous.  Get all the
   _fields_ descriptors from descr->proto, create new descriptors with offset
   and index adjusted, and stuff them into type.
   */
  static int
  MakeFields(PyObject *type, CFieldObject *descr,
             Py_ssize_t index, Py_ssize_t offset)
  {
      Py_ssize_t i;
      PyObject *fields;
      PyObject *fieldlist;
  
      fields = PyObject_GetAttrString(descr->proto, "_fields_");
      if (fields == NULL)
          return -1;
      fieldlist = PySequence_Fast(fields, "_fields_ must be a sequence");
      Py_DECREF(fields);
      if (fieldlist == NULL)
          return -1;
  
      for (i = 0; i < PySequence_Fast_GET_SIZE(fieldlist); ++i) {
          PyObject *pair = PySequence_Fast_GET_ITEM(fieldlist, i); /* borrowed */
          PyObject *fname, *ftype, *bits;
          CFieldObject *fdescr;
          CFieldObject *new_descr;
          /* Convert to PyArg_UnpackTuple... */
          if (!PyArg_ParseTuple(pair, "OO|O", &fname, &ftype, &bits)) {
              Py_DECREF(fieldlist);
              return -1;
          }
          fdescr = (CFieldObject *)PyObject_GetAttr(descr->proto, fname);
          if (fdescr == NULL) {
              Py_DECREF(fieldlist);
              return -1;
          }
          if (Py_TYPE(fdescr) != &PyCField_Type) {
              PyErr_SetString(PyExc_TypeError, "unexpected type");
              Py_DECREF(fdescr);
              Py_DECREF(fieldlist);
              return -1;
          }
          if (fdescr->anonymous) {
              int rc = MakeFields(type, fdescr,
                                  index + fdescr->index,
                                  offset + fdescr->offset);
              Py_DECREF(fdescr);
              if (rc == -1) {
                  Py_DECREF(fieldlist);
                  return -1;
              }
              continue;
          }
          new_descr = (CFieldObject *)PyObject_CallObject((PyObject *)&PyCField_Type, NULL);
          if (new_descr == NULL) {
              Py_DECREF(fdescr);
              Py_DECREF(fieldlist);
              return -1;
          }
          assert(Py_TYPE(new_descr) == &PyCField_Type);
          new_descr->size = fdescr->size;
          new_descr->offset = fdescr->offset + offset;
          new_descr->index = fdescr->index + index;
          new_descr->proto = fdescr->proto;
          Py_XINCREF(new_descr->proto);
          new_descr->getfunc = fdescr->getfunc;
          new_descr->setfunc = fdescr->setfunc;
  
          Py_DECREF(fdescr);
  
          if (-1 == PyObject_SetAttr(type, fname, (PyObject *)new_descr)) {
              Py_DECREF(fieldlist);
              Py_DECREF(new_descr);
              return -1;
          }
          Py_DECREF(new_descr);
      }
      Py_DECREF(fieldlist);
      return 0;
  }
  
  /* Iterate over the names in the type's _anonymous_ attribute, if present,
   */
  static int
  MakeAnonFields(PyObject *type)
  {
      PyObject *anon;
      PyObject *anon_names;
      Py_ssize_t i;
  
      anon = PyObject_GetAttrString(type, "_anonymous_");
      if (anon == NULL) {
          PyErr_Clear();
          return 0;
      }
      anon_names = PySequence_Fast(anon, "_anonymous_ must be a sequence");
      Py_DECREF(anon);
      if (anon_names == NULL)
          return -1;
  
      for (i = 0; i < PySequence_Fast_GET_SIZE(anon_names); ++i) {
          PyObject *fname = PySequence_Fast_GET_ITEM(anon_names, i); /* borrowed */
          CFieldObject *descr = (CFieldObject *)PyObject_GetAttr(type, fname);
          if (descr == NULL) {
              Py_DECREF(anon_names);
              return -1;
          }
          assert(Py_TYPE(descr) == &PyCField_Type);
          descr->anonymous = 1;
  
          /* descr is in the field descriptor. */
          if (-1 == MakeFields(type, (CFieldObject *)descr,
                               ((CFieldObject *)descr)->index,
                               ((CFieldObject *)descr)->offset)) {
              Py_DECREF(descr);
              Py_DECREF(anon_names);
              return -1;
          }
          Py_DECREF(descr);
      }
  
      Py_DECREF(anon_names);
      return 0;
  }
  
  /*
    Retrive the (optional) _pack_ attribute from a type, the _fields_ attribute,
    and create an StgDictObject.  Used for Structure and Union subclasses.
  */
  int
  PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct)
  {
      StgDictObject *stgdict, *basedict;
      Py_ssize_t len, offset, size, align, i;
      Py_ssize_t union_size, total_align;
      Py_ssize_t field_size = 0;
      int bitofs;
      PyObject *isPacked;
      int pack = 0;
      Py_ssize_t ffi_ofs;
      int big_endian;
  
      /* HACK Alert: I cannot be bothered to fix ctypes.com, so there has to
         be a way to use the old, broken sematics: _fields_ are not extended
         but replaced in subclasses.
  
         XXX Remove this in ctypes 1.0!
      */
      int use_broken_old_ctypes_semantics;
  
      if (fields == NULL)
          return 0;
  
  #ifdef WORDS_BIGENDIAN
      big_endian = PyObject_HasAttrString(type, "_swappedbytes_") ? 0 : 1;
  #else
      big_endian = PyObject_HasAttrString(type, "_swappedbytes_") ? 1 : 0;
  #endif
  
      use_broken_old_ctypes_semantics = \
          PyObject_HasAttrString(type, "_use_broken_old_ctypes_structure_semantics_");
  
      isPacked = PyObject_GetAttrString(type, "_pack_");
      if (isPacked) {
          pack = _PyLong_AsInt(isPacked);
          if (pack < 0 || PyErr_Occurred()) {
              Py_XDECREF(isPacked);
              PyErr_SetString(PyExc_ValueError,
                              "_pack_ must be a non-negative integer");
              return -1;
          }
          Py_DECREF(isPacked);
      } else
          PyErr_Clear();
  
      len = PySequence_Length(fields);
      if (len == -1) {
          PyErr_SetString(PyExc_TypeError,
                          "'_fields_' must be a sequence of pairs");
          return -1;
      }
  
      stgdict = PyType_stgdict(type);
      if (!stgdict)
          return -1;
      /* If this structure/union is already marked final we cannot assign
         _fields_ anymore. */
  
      if (stgdict->flags & DICTFLAG_FINAL) {/* is final ? */
          PyErr_SetString(PyExc_AttributeError,
                          "_fields_ is final");
          return -1;
      }
  
      if (stgdict->format) {
          PyMem_Free(stgdict->format);
          stgdict->format = NULL;
      }
  
      if (stgdict->ffi_type_pointer.elements)
          PyMem_Free(stgdict->ffi_type_pointer.elements);
  
      basedict = PyType_stgdict((PyObject *)((PyTypeObject *)type)->tp_base);
      if (basedict && !use_broken_old_ctypes_semantics) {
          size = offset = basedict->size;
          align = basedict->align;
          union_size = 0;
          total_align = align ? align : 1;
          stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT;
          stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, basedict->length + len + 1);
          if (stgdict->ffi_type_pointer.elements == NULL) {
              PyErr_NoMemory();
              return -1;
          }
          memset(stgdict->ffi_type_pointer.elements, 0,
                 sizeof(ffi_type *) * (basedict->length + len + 1));
          memcpy(stgdict->ffi_type_pointer.elements,
                 basedict->ffi_type_pointer.elements,
                 sizeof(ffi_type *) * (basedict->length));
          ffi_ofs = basedict->length;
      } else {
          offset = 0;
          size = 0;
          align = 0;
          union_size = 0;
          total_align = 1;
          stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT;
          stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, len + 1);
          if (stgdict->ffi_type_pointer.elements == NULL) {
              PyErr_NoMemory();
              return -1;
          }
          memset(stgdict->ffi_type_pointer.elements, 0,
                 sizeof(ffi_type *) * (len + 1));
          ffi_ofs = 0;
      }
  
      assert(stgdict->format == NULL);
      if (isStruct && !isPacked) {
          stgdict->format = _ctypes_alloc_format_string(NULL, "T{");
      } else {
          /* PEP3118 doesn't support union, or packed structures (well,
             only standard packing, but we dont support the pep for
             that). Use 'B' for bytes. */
          stgdict->format = _ctypes_alloc_format_string(NULL, "B");
      }
      if (stgdict->format == NULL)
          return -1;
  
  #define realdict ((PyObject *)&stgdict->dict)
      for (i = 0; i < len; ++i) {
          PyObject *name = NULL, *desc = NULL;
          PyObject *pair = PySequence_GetItem(fields, i);
          PyObject *prop;
          StgDictObject *dict;
          int bitsize = 0;
  
          if (!pair || !PyArg_ParseTuple(pair, "UO|i", &name, &desc, &bitsize)) {
              PyErr_SetString(PyExc_TypeError,
                              "'_fields_' must be a sequence of (name, C type) pairs");
              Py_XDECREF(pair);
              return -1;
          }
          dict = PyType_stgdict(desc);
          if (dict == NULL) {
              Py_DECREF(pair);
              PyErr_Format(PyExc_TypeError,
                           "second item in _fields_ tuple (index %zd) must be a C type",
                           i);
              return -1;
          }
          stgdict->ffi_type_pointer.elements[ffi_ofs + i] = &dict->ffi_type_pointer;
          if (dict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER))
              stgdict->flags |= TYPEFLAG_HASPOINTER;
          dict->flags |= DICTFLAG_FINAL; /* mark field type final */
          if (PyTuple_Size(pair) == 3) { /* bits specified */
              switch(dict->ffi_type_pointer.type) {
              case FFI_TYPE_UINT8:
              case FFI_TYPE_UINT16:
              case FFI_TYPE_UINT32:
              case FFI_TYPE_SINT64:
              case FFI_TYPE_UINT64:
                  break;
  
              case FFI_TYPE_SINT8:
              case FFI_TYPE_SINT16:
              case FFI_TYPE_SINT32:
                  if (dict->getfunc != _ctypes_get_fielddesc("c")->getfunc
  #ifdef CTYPES_UNICODE
                      && dict->getfunc != _ctypes_get_fielddesc("u")->getfunc
  #endif
                      )
                      break;
                  /* else fall through */
              default:
                  PyErr_Format(PyExc_TypeError,
                               "bit fields not allowed for type %s",
                               ((PyTypeObject *)desc)->tp_name);
                  Py_DECREF(pair);
                  return -1;
              }
              if (bitsize <= 0 || bitsize > dict->size * 8) {
                  PyErr_SetString(PyExc_ValueError,
                                  "number of bits invalid for bit field");
                  Py_DECREF(pair);
                  return -1;
              }
          } else
              bitsize = 0;
  
          if (isStruct && !isPacked) {
              char *fieldfmt = dict->format ? dict->format : "B";
              char *fieldname = _PyUnicode_AsString(name);
              char *ptr;
              Py_ssize_t len;
              char *buf;
  
              if (fieldname == NULL)
              {
                  Py_DECREF(pair);
                  return -1;
              }
  
              len = strlen(fieldname) + strlen(fieldfmt);
  
              buf = PyMem_Malloc(len + 2 + 1);
              if (buf == NULL) {
                  Py_DECREF(pair);
                  PyErr_NoMemory();
                  return -1;
              }
              sprintf(buf, "%s:%s:", fieldfmt, fieldname);
  
              ptr = stgdict->format;
              if (dict->shape != NULL) {
                  stgdict->format = _ctypes_alloc_format_string_with_shape(
                      dict->ndim, dict->shape, stgdict->format, buf);
              } else {
                  stgdict->format = _ctypes_alloc_format_string(stgdict->format, buf);
              }
              PyMem_Free(ptr);
              PyMem_Free(buf);
  
              if (stgdict->format == NULL) {
                  Py_DECREF(pair);
                  return -1;
              }
          }
  
          if (isStruct) {
              prop = PyCField_FromDesc(desc, i,
                                     &field_size, bitsize, &bitofs,
                                     &size, &offset, &align,
                                     pack, big_endian);
          } else /* union */ {
              size = 0;
              offset = 0;
              align = 0;
              prop = PyCField_FromDesc(desc, i,
                                     &field_size, bitsize, &bitofs,
                                     &size, &offset, &align,
                                     pack, big_endian);
              union_size = max(size, union_size);
          }
          total_align = max(align, total_align);
  
          if (!prop) {
              Py_DECREF(pair);
              return -1;
          }
          if (-1 == PyObject_SetAttr(type, name, prop)) {
              Py_DECREF(prop);
              Py_DECREF(pair);
              return -1;
          }
          Py_DECREF(pair);
          Py_DECREF(prop);
      }
  #undef realdict
  
      if (isStruct && !isPacked) {
          char *ptr = stgdict->format;
          stgdict->format = _ctypes_alloc_format_string(stgdict->format, "}");
          PyMem_Free(ptr);
          if (stgdict->format == NULL)
              return -1;
      }
  
      if (!isStruct)
          size = union_size;
  
      /* Adjust the size according to the alignment requirements */
      size = ((size + total_align - 1) / total_align) * total_align;
  
      stgdict->ffi_type_pointer.alignment = Py_SAFE_DOWNCAST(total_align,
                                                             Py_ssize_t,
                                                             unsigned short);
      stgdict->ffi_type_pointer.size = size;
  
      stgdict->size = size;
      stgdict->align = total_align;
      stgdict->length = len;      /* ADD ffi_ofs? */
  
      /* We did check that this flag was NOT set above, it must not
         have been set until now. */
      if (stgdict->flags & DICTFLAG_FINAL) {
          PyErr_SetString(PyExc_AttributeError,
                          "Structure or union cannot contain itself");
          return -1;
      }
      stgdict->flags |= DICTFLAG_FINAL;
  
      return MakeAnonFields(type);
  }
  