/////////////// ArrayAPI.proto ///////////////

// arrayarray.h
//
//    Artificial C-API for Python's <array.array> type,
//    used by array.pxd
//
//    last changes: 2009-05-15 rk
//                  2012-05-02 andreasvc
//                  (see revision control)
//

#ifndef _ARRAYARRAY_H
#define _ARRAYARRAY_H

// These two forward declarations are explicitly handled in the type
// declaration code, as including them here is too late for cython-defined
// types to use them.
// struct arrayobject;
// typedef struct arrayobject arrayobject;

// All possible arraydescr values are defined in the vector "descriptors"
// below.  That's defined later because the appropriate get and set
// functions aren't visible yet.
typedef struct arraydescr {
    int typecode;
    int itemsize;
    PyObject * (*getitem)(struct arrayobject *, Py_ssize_t);
    int (*setitem)(struct arrayobject *, Py_ssize_t, PyObject *);
#if PY_MAJOR_VERSION >= 3
    char *formats;
#endif    
} arraydescr;


struct arrayobject {
    PyObject_HEAD
    Py_ssize_t ob_size;
    union {
        char *ob_item;
        float *as_floats;
        double *as_doubles;
        int *as_ints;
        unsigned int *as_uints;
        unsigned char *as_uchars;
        signed char *as_schars;
        char *as_chars;
        unsigned long *as_ulongs;
        long *as_longs;
        short *as_shorts;
        unsigned short *as_ushorts;
        Py_UNICODE *as_pyunicodes;
        void *as_voidptr;
    } data;
    Py_ssize_t allocated;
    struct arraydescr *ob_descr;
    PyObject *weakreflist; /* List of weak references */
#if PY_MAJOR_VERSION >= 3
        int ob_exports;  /* Number of exported buffers */
#endif
};

#ifndef NO_NEWARRAY_INLINE
//  fast creation of a new array
static CYTHON_INLINE PyObject * newarrayobject(PyTypeObject *type, Py_ssize_t size,
    struct arraydescr *descr) {
    arrayobject *op;
    size_t nbytes;

    if (size < 0) {
        PyErr_BadInternalCall();
        return NULL;
    }

    nbytes = size * descr->itemsize;
    // Check for overflow
    if (nbytes / descr->itemsize != (size_t)size) {
        return PyErr_NoMemory();
    }
    op = (arrayobject *) type->tp_alloc(type, 0);
    if (op == NULL) {
        return NULL;
    }
    op->ob_descr = descr;
    op->allocated = size;
    op->weakreflist = NULL;
    op->ob_size = size;
    if (size <= 0) {
        op->data.ob_item = NULL;
    }
    else {
        op->data.ob_item = PyMem_NEW(char, nbytes);
        if (op->data.ob_item == NULL) {
            Py_DECREF(op);
            return PyErr_NoMemory();
        }
    }
    return (PyObject *) op;
}
#else
PyObject* newarrayobject(PyTypeObject *type, Py_ssize_t size,
    struct arraydescr *descr);
#endif /* ifndef NO_NEWARRAY_INLINE */

// fast resize (reallocation to the point)
// not designed for filing small increments (but for fast opaque array apps)
static CYTHON_INLINE int resize(arrayobject *self, Py_ssize_t n) {
    void *items = (void*) self->data.ob_item;
    PyMem_Resize(items, char, (size_t)(n * self->ob_descr->itemsize));
    if (items == NULL) {
        PyErr_NoMemory();
        return -1;
    }    
    self->data.ob_item = (char*) items;
    self->ob_size = n;
    self->allocated = n;
    return 0;
}

// suitable for small increments; over allocation 50% ;
static CYTHON_INLINE int resize_smart(arrayobject *self, Py_ssize_t n) {
    void *items = (void*) self->data.ob_item;
    Py_ssize_t newsize;
    if (n < self->allocated) {
        if (n*4 > self->allocated) {
            self->ob_size = n;
            return 0;
        }
    }
    newsize = n  * 3 / 2 + 1;
    PyMem_Resize(items, char, (size_t)(newsize * self->ob_descr->itemsize));
    if (items == NULL) {
        PyErr_NoMemory();
        return -1;
    }    
    self->data.ob_item = (char*) items;
    self->ob_size = n;
    self->allocated = newsize;
    return 0;
}

#endif
/* _ARRAYARRAY_H */
