1"""
2  array.pxd
3  
4  Cython interface to Python's array.array module.
5  
6  * 1D contiguous data view
7  * tools for fast array creation, maximum C-speed and handiness
8  * suitable as allround light weight auto-array within Cython code too
9  
10  Usage:
11  
12  >>> cimport array
13
14  Usage through Cython buffer interface (Py2.3+):  
15  
16    >>> def f(arg1, unsigned i, double dx)
17    ...     array.array[double] a = arg1
18    ...     a[i] += dx
19  
20  Fast C-level new_array(_zeros), resize_array, copy_array, Py_SIZE(obj),
21  zero_array
22  
23    cdef array.array[double] k = array.copy(d) 
24    cdef array.array[double] n = array.array(d, Py_SIZE(d) * 2 )
25    cdef array.array[double] m = array.zeros_like(FLOAT_TEMPLATE)
26    array.resize(f, 200000)
27  
28  Zero overhead with naked data pointer views by union: 
29  _f, _d, _i, _c, _u, ... 
30  => Original C array speed + Python dynamic memory management
31
32    cdef array.array a = inarray
33    if 
34    a._d[2] += 0.66   # use as double array without extra casting
35  
36    float *subview = vector._f + 10  # starting from 10th element
37    unsigned char *subview_buffer = vector._B + 4  
38    
39  Suitable as lightweight arrays intra Cython without speed penalty. 
40  Replacement for C stack/malloc arrays; no trouble with refcounting, 
41  mem.leaks; seamless Python compatibility, buffer() optional
42  
43
44  last changes: 2009-05-15 rk
45              : 2009-12-06 bp
46              : 2012-05-02 andreasvc
47              : (see revision control)
48"""
49from libc.string cimport strcat, strncat, \
50    memset, memchr, memcmp, memcpy, memmove
51
52from cpython.object cimport Py_SIZE
53from cpython.ref cimport PyTypeObject, Py_TYPE
54from cpython.exc cimport PyErr_BadArgument
55from cpython.mem cimport PyMem_Malloc, PyMem_Free
56
57cdef extern from *:  # Hard-coded utility code hack.
58    ctypedef class array.array [object arrayobject]
59    ctypedef object GETF(array a, Py_ssize_t ix)
60    ctypedef object SETF(array a, Py_ssize_t ix, object o)
61    ctypedef struct arraydescr:  # [object arraydescr]:
62            int typecode
63            int itemsize
64            GETF getitem    # PyObject * (*getitem)(struct arrayobject *, Py_ssize_t);
65            SETF setitem    # int (*setitem)(struct arrayobject *, Py_ssize_t, PyObject *);
66
67    ctypedef union __data_union:
68        # views of ob_item:
69        float* as_floats        # direct float pointer access to buffer
70        double* as_doubles      # double ...
71        int*    as_ints
72        unsigned int *as_uints
73        unsigned char *as_uchars
74        signed char *as_schars
75        char *as_chars
76        unsigned long *as_ulongs
77        long *as_longs
78        short *as_shorts
79        unsigned short *as_ushorts
80        Py_UNICODE *as_pyunicodes
81        void *as_voidptr
82
83    ctypedef class array.array [object arrayobject]:
84        cdef __cythonbufferdefaults__ = {'ndim' : 1, 'mode':'c'}
85
86        cdef:
87            Py_ssize_t ob_size
88            arraydescr* ob_descr    # struct arraydescr *ob_descr;
89            __data_union data
90
91        def __getbuffer__(self, Py_buffer* info, int flags):
92            # This implementation of getbuffer is geared towards Cython
93            # requirements, and does not yet fullfill the PEP.
94            # In particular strided access is always provided regardless
95            # of flags
96            item_count = Py_SIZE(self)
97
98            info.suboffsets = NULL
99            info.buf = self.data.as_chars
100            info.readonly = 0
101            info.ndim = 1
102            info.itemsize = self.ob_descr.itemsize   # e.g. sizeof(float)
103            info.len = info.itemsize * item_count
104
105            info.shape = <Py_ssize_t*> PyMem_Malloc(sizeof(Py_ssize_t) + 2)
106            if not info.shape:
107                raise MemoryError()
108            info.shape[0] = item_count      # constant regardless of resizing
109            info.strides = &info.itemsize
110
111            info.format = <char*> (info.shape + 1)
112            info.format[0] = self.ob_descr.typecode
113            info.format[1] = 0
114            info.obj = self
115
116        def __releasebuffer__(self, Py_buffer* info):
117            PyMem_Free(info.shape)
118
119    array newarrayobject(PyTypeObject* type, Py_ssize_t size, arraydescr *descr)
120
121    # fast resize/realloc
122    # not suitable for small increments; reallocation 'to the point'
123    int resize(array self, Py_ssize_t n) except -1
124    # efficient for small increments (not in Py2.3-)
125    int resize_smart(array self, Py_ssize_t n) except -1
126
127
128cdef inline array clone(array template, Py_ssize_t length, bint zero):
129    """ fast creation of a new array, given a template array.
130    type will be same as template.
131    if zero is true, new array will be initialized with zeroes."""
132    op = newarrayobject(Py_TYPE(template), length, template.ob_descr)
133    if zero and op is not None:
134        memset(op.data.as_chars, 0, length * op.ob_descr.itemsize)
135    return op
136
137cdef inline array copy(array self):
138    """ make a copy of an array. """
139    op = newarrayobject(Py_TYPE(self), Py_SIZE(self), self.ob_descr)
140    memcpy(op.data.as_chars, self.data.as_chars, Py_SIZE(op) * op.ob_descr.itemsize)
141    return op
142
143cdef inline int extend_buffer(array self, char* stuff, Py_ssize_t n) except -1:
144    """ efficent appending of new stuff of same type
145    (e.g. of same array type)
146    n: number of elements (not number of bytes!) """
147    cdef Py_ssize_t itemsize = self.ob_descr.itemsize
148    cdef Py_ssize_t origsize = Py_SIZE(self)
149    resize_smart(self, origsize + n)
150    memcpy(self.data.as_chars + origsize * itemsize, stuff, n * itemsize)
151    return 0
152
153cdef inline int extend(array self, array other) except -1:
154    """ extend array with data from another array; types must match. """
155    if self.ob_descr.typecode != other.ob_descr.typecode:
156        PyErr_BadArgument()
157    return extend_buffer(self, other.data.as_chars, Py_SIZE(other))
158
159cdef inline void zero(array self):
160    """ set all elements of array to zero. """
161    memset(self.data.as_chars, 0, Py_SIZE(self) * self.ob_descr.itemsize)
162