1#################### View.MemoryView ####################
2
3# This utility provides cython.array and cython.view.memoryview
4
5import cython
6
7# from cpython cimport ...
8cdef extern from "Python.h":
9    int PyIndex_Check "__Pyx_PyIndex_Check" (object)
10    object PyLong_FromVoidPtr(void *)
11
12cdef extern from "pythread.h":
13    ctypedef void *PyThread_type_lock
14
15    PyThread_type_lock PyThread_allocate_lock()
16    void PyThread_free_lock(PyThread_type_lock)
17    int PyThread_acquire_lock(PyThread_type_lock, int mode) nogil
18    void PyThread_release_lock(PyThread_type_lock) nogil
19
20cdef extern from "string.h":
21    void *memset(void *b, int c, size_t len)
22
23cdef extern from *:
24    int __Pyx_GetBuffer(object, Py_buffer *, int) except -1
25    void __Pyx_ReleaseBuffer(Py_buffer *)
26
27    ctypedef struct PyObject
28    ctypedef Py_ssize_t Py_intptr_t
29    void Py_INCREF(PyObject *)
30    void Py_DECREF(PyObject *)
31
32    void* PyMem_Malloc(size_t n)
33    void PyMem_Free(void *p)
34
35    cdef struct __pyx_memoryview "__pyx_memoryview_obj":
36        Py_buffer view
37        PyObject *obj
38        __Pyx_TypeInfo *typeinfo
39
40    ctypedef struct {{memviewslice_name}}:
41        __pyx_memoryview *memview
42        char *data
43        Py_ssize_t shape[{{max_dims}}]
44        Py_ssize_t strides[{{max_dims}}]
45        Py_ssize_t suboffsets[{{max_dims}}]
46
47    void __PYX_INC_MEMVIEW({{memviewslice_name}} *memslice, int have_gil)
48    void __PYX_XDEC_MEMVIEW({{memviewslice_name}} *memslice, int have_gil)
49
50    ctypedef struct __pyx_buffer "Py_buffer":
51        PyObject *obj
52
53    PyObject *Py_None
54
55    cdef enum:
56        PyBUF_C_CONTIGUOUS,
57        PyBUF_F_CONTIGUOUS,
58        PyBUF_ANY_CONTIGUOUS
59        PyBUF_FORMAT
60        PyBUF_WRITABLE
61        PyBUF_STRIDES
62        PyBUF_INDIRECT
63        PyBUF_RECORDS
64
65    ctypedef struct __Pyx_TypeInfo:
66        pass
67
68    cdef object capsule "__pyx_capsule_create" (void *p, char *sig)
69    cdef int __pyx_array_getbuffer(PyObject *obj, Py_buffer view, int flags)
70    cdef int __pyx_memoryview_getbuffer(PyObject *obj, Py_buffer view, int flags)
71
72cdef extern from *:
73    ctypedef int __pyx_atomic_int
74    {{memviewslice_name}} slice_copy_contig "__pyx_memoryview_copy_new_contig"(
75                                 __Pyx_memviewslice *from_mvs,
76                                 char *mode, int ndim,
77                                 size_t sizeof_dtype, int contig_flag,
78                                 bint dtype_is_object) nogil except *
79    bint slice_is_contig "__pyx_memviewslice_is_contig" (
80                            {{memviewslice_name}} *mvs, char order, int ndim) nogil
81    bint slices_overlap "__pyx_slices_overlap" ({{memviewslice_name}} *slice1,
82                                                {{memviewslice_name}} *slice2,
83                                                int ndim, size_t itemsize) nogil
84
85
86cdef extern from "stdlib.h":
87    void *malloc(size_t) nogil
88    void free(void *) nogil
89    void *memcpy(void *dest, void *src, size_t n) nogil
90
91
92
93
94#
95### cython.array class
96#
97
98@cname("__pyx_array")
99cdef class array:
100
101    cdef:
102        char *data
103        Py_ssize_t len
104        char *format
105        int ndim
106        Py_ssize_t *_shape
107        Py_ssize_t *_strides
108        Py_ssize_t itemsize
109        unicode mode  # FIXME: this should have been a simple 'char'
110        bytes _format
111        void (*callback_free_data)(void *data)
112        # cdef object _memview
113        cdef bint free_data
114        cdef bint dtype_is_object
115
116    def __cinit__(array self, tuple shape, Py_ssize_t itemsize, format not None,
117                  mode="c", bint allocate_buffer=True):
118
119        cdef int idx
120        cdef Py_ssize_t i, dim
121        cdef PyObject **p
122
123        self.ndim = <int> len(shape)
124        self.itemsize = itemsize
125
126        if not self.ndim:
127            raise ValueError("Empty shape tuple for cython.array")
128
129        if itemsize <= 0:
130            raise ValueError("itemsize <= 0 for cython.array")
131
132        if isinstance(format, unicode):
133            format = (<unicode>format).encode('ASCII')
134        self._format = format  # keep a reference to the byte string
135        self.format = self._format
136
137        # use single malloc() for both shape and strides
138        self._shape = <Py_ssize_t *> PyMem_Malloc(sizeof(Py_ssize_t)*self.ndim*2)
139        self._strides = self._shape + self.ndim
140
141        if not self._shape:
142            raise MemoryError("unable to allocate shape and strides.")
143
144        # cdef Py_ssize_t dim, stride
145        for idx, dim in enumerate(shape):
146            if dim <= 0:
147                raise ValueError("Invalid shape in axis %d: %d." % (idx, dim))
148            self._shape[idx] = dim
149
150        cdef char order
151        if mode == 'fortran':
152            order = b'F'
153            self.mode = u'fortran'
154        elif mode == 'c':
155            order = b'C'
156            self.mode = u'c'
157        else:
158            raise ValueError("Invalid mode, expected 'c' or 'fortran', got %s" % mode)
159
160        self.len = fill_contig_strides_array(self._shape, self._strides,
161                                             itemsize, self.ndim, order)
162
163        self.free_data = allocate_buffer
164        self.dtype_is_object = format == b'O'
165        if allocate_buffer:
166            # use malloc() for backwards compatibility
167            # in case external code wants to change the data pointer
168            self.data = <char *>malloc(self.len)
169            if not self.data:
170                raise MemoryError("unable to allocate array data.")
171
172            if self.dtype_is_object:
173                p = <PyObject **> self.data
174                for i in range(self.len / itemsize):
175                    p[i] = Py_None
176                    Py_INCREF(Py_None)
177
178    @cname('getbuffer')
179    def __getbuffer__(self, Py_buffer *info, int flags):
180        cdef int bufmode = -1
181        if self.mode == u"c":
182            bufmode = PyBUF_C_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS
183        elif self.mode == u"fortran":
184            bufmode = PyBUF_F_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS
185        if not (flags & bufmode):
186            raise ValueError("Can only create a buffer that is contiguous in memory.")
187        info.buf = self.data
188        info.len = self.len
189        info.ndim = self.ndim
190        info.shape = self._shape
191        info.strides = self._strides
192        info.suboffsets = NULL
193        info.itemsize = self.itemsize
194        info.readonly = 0
195
196        if flags & PyBUF_FORMAT:
197            info.format = self.format
198        else:
199            info.format = NULL
200
201        info.obj = self
202
203    __pyx_getbuffer = capsule(<void *> &__pyx_array_getbuffer, "getbuffer(obj, view, flags)")
204
205    def __dealloc__(array self):
206        if self.callback_free_data != NULL:
207            self.callback_free_data(self.data)
208        elif self.free_data:
209            if self.dtype_is_object:
210                refcount_objects_in_slice(self.data, self._shape,
211                                          self._strides, self.ndim, False)
212            free(self.data)
213        PyMem_Free(self._shape)
214
215    property memview:
216        @cname('get_memview')
217        def __get__(self):
218            # Make this a property as 'self.data' may be set after instantiation
219            flags =  PyBUF_ANY_CONTIGUOUS|PyBUF_FORMAT|PyBUF_WRITABLE
220            return  memoryview(self, flags, self.dtype_is_object)
221
222
223    def __getattr__(self, attr):
224        return getattr(self.memview, attr)
225
226    def __getitem__(self, item):
227        return self.memview[item]
228
229    def __setitem__(self, item, value):
230        self.memview[item] = value
231
232
233@cname("__pyx_array_new")
234cdef array array_cwrapper(tuple shape, Py_ssize_t itemsize, char *format,
235                          char *mode, char *buf):
236    cdef array result
237
238    if buf == NULL:
239        result = array(shape, itemsize, format, mode.decode('ASCII'))
240    else:
241        result = array(shape, itemsize, format, mode.decode('ASCII'),
242                       allocate_buffer=False)
243        result.data = buf
244
245    return result
246
247
248#
249### Memoryview constants and cython.view.memoryview class
250#
251
252# Disable generic_contiguous, as it makes trouble verifying contiguity:
253#   - 'contiguous' or '::1' means the dimension is contiguous with dtype
254#   - 'indirect_contiguous' means a contiguous list of pointers
255#   - dtype contiguous must be contiguous in the first or last dimension
256#     from the start, or from the dimension following the last indirect dimension
257#
258#   e.g.
259#           int[::indirect_contiguous, ::contiguous, :]
260#
261#   is valid (list of pointers to 2d fortran-contiguous array), but
262#
263#           int[::generic_contiguous, ::contiguous, :]
264#
265#   would mean you'd have assert dimension 0 to be indirect (and pointer contiguous) at runtime.
266#   So it doesn't bring any performance benefit, and it's only confusing.
267
268@cname('__pyx_MemviewEnum')
269cdef class Enum(object):
270    cdef object name
271    def __init__(self, name):
272        self.name = name
273    def __repr__(self):
274        return self.name
275
276cdef generic = Enum("<strided and direct or indirect>")
277cdef strided = Enum("<strided and direct>") # default
278cdef indirect = Enum("<strided and indirect>")
279# Disable generic_contiguous, as it is a troublemaker
280#cdef generic_contiguous = Enum("<contiguous and direct or indirect>")
281cdef contiguous = Enum("<contiguous and direct>")
282cdef indirect_contiguous = Enum("<contiguous and indirect>")
283
284# 'follow' is implied when the first or last axis is ::1
285
286
287@cname('__pyx_align_pointer')
288cdef void *align_pointer(void *memory, size_t alignment) nogil:
289    "Align pointer memory on a given boundary"
290    cdef Py_intptr_t aligned_p = <Py_intptr_t> memory
291    cdef size_t offset
292
293    with cython.cdivision(True):
294        offset = aligned_p % alignment
295
296    if offset > 0:
297        aligned_p += alignment - offset
298
299    return <void *> aligned_p
300
301@cname('__pyx_memoryview')
302cdef class memoryview(object):
303
304    cdef object obj
305    cdef object _size
306    cdef object _array_interface
307    cdef PyThread_type_lock lock
308    # the following array will contain a single __pyx_atomic int with
309    # suitable alignment
310    cdef __pyx_atomic_int acquisition_count[2]
311    cdef __pyx_atomic_int *acquisition_count_aligned_p
312    cdef Py_buffer view
313    cdef int flags
314    cdef bint dtype_is_object
315    cdef __Pyx_TypeInfo *typeinfo
316
317    def __cinit__(memoryview self, object obj, int flags, bint dtype_is_object=False):
318        self.obj = obj
319        self.flags = flags
320        if type(self) is memoryview or obj is not None:
321            __Pyx_GetBuffer(obj, &self.view, flags)
322            if <PyObject *> self.view.obj == NULL:
323                (<__pyx_buffer *> &self.view).obj = Py_None
324                Py_INCREF(Py_None)
325
326        self.lock = PyThread_allocate_lock()
327        if self.lock == NULL:
328            raise MemoryError
329
330        if flags & PyBUF_FORMAT:
331            self.dtype_is_object = self.view.format == b'O'
332        else:
333            self.dtype_is_object = dtype_is_object
334
335        self.acquisition_count_aligned_p = <__pyx_atomic_int *> align_pointer(
336                  <void *> &self.acquisition_count[0], sizeof(__pyx_atomic_int))
337        self.typeinfo = NULL
338
339    def __dealloc__(memoryview self):
340        if self.obj is not None:
341            __Pyx_ReleaseBuffer(&self.view)
342
343        if self.lock != NULL:
344            PyThread_free_lock(self.lock)
345
346    cdef char *get_item_pointer(memoryview self, object index) except NULL:
347        cdef Py_ssize_t dim
348        cdef char *itemp = <char *> self.view.buf
349
350        for dim, idx in enumerate(index):
351            itemp = pybuffer_index(&self.view, itemp, idx, dim)
352
353        return itemp
354
355    #@cname('__pyx_memoryview_getitem')
356    def __getitem__(memoryview self, object index):
357        if index is Ellipsis:
358            return self
359
360        have_slices, indices = _unellipsify(index, self.view.ndim)
361
362        cdef char *itemp
363        if have_slices:
364            return memview_slice(self, indices)
365        else:
366            itemp = self.get_item_pointer(indices)
367            return self.convert_item_to_object(itemp)
368
369    def __setitem__(memoryview self, object index, object value):
370        have_slices, index = _unellipsify(index, self.view.ndim)
371
372        if have_slices:
373            obj = self.is_slice(value)
374            if obj:
375                self.setitem_slice_assignment(self[index], obj)
376            else:
377                self.setitem_slice_assign_scalar(self[index], value)
378        else:
379            self.setitem_indexed(index, value)
380
381    cdef is_slice(self, obj):
382        if not isinstance(obj, memoryview):
383            try:
384                obj = memoryview(obj, self.flags|PyBUF_ANY_CONTIGUOUS,
385                                 self.dtype_is_object)
386            except TypeError:
387                return None
388
389        return obj
390
391    cdef setitem_slice_assignment(self, dst, src):
392        cdef {{memviewslice_name}} dst_slice
393        cdef {{memviewslice_name}} src_slice
394
395        memoryview_copy_contents(get_slice_from_memview(src, &src_slice)[0],
396                                 get_slice_from_memview(dst, &dst_slice)[0],
397                                 src.ndim, dst.ndim, self.dtype_is_object)
398
399    cdef setitem_slice_assign_scalar(self, memoryview dst, value):
400        cdef int array[128]
401        cdef void *tmp = NULL
402        cdef void *item
403
404        cdef {{memviewslice_name}} *dst_slice
405        cdef {{memviewslice_name}} tmp_slice
406        dst_slice = get_slice_from_memview(dst, &tmp_slice)
407
408        if <size_t>self.view.itemsize > sizeof(array):
409            tmp = PyMem_Malloc(self.view.itemsize)
410            if tmp == NULL:
411                raise MemoryError
412            item = tmp
413        else:
414            item = <void *> array
415
416        try:
417            if self.dtype_is_object:
418                (<PyObject **> item)[0] = <PyObject *> value
419            else:
420                self.assign_item_from_object(<char *> item, value)
421
422            # It would be easy to support indirect dimensions, but it's easier
423            # to disallow :)
424            if self.view.suboffsets != NULL:
425                assert_direct_dimensions(self.view.suboffsets, self.view.ndim)
426            slice_assign_scalar(dst_slice, dst.view.ndim, self.view.itemsize,
427                                item, self.dtype_is_object)
428        finally:
429            PyMem_Free(tmp)
430
431    cdef setitem_indexed(self, index, value):
432        cdef char *itemp = self.get_item_pointer(index)
433        self.assign_item_from_object(itemp, value)
434
435    cdef convert_item_to_object(self, char *itemp):
436        """Only used if instantiated manually by the user, or if Cython doesn't
437        know how to convert the type"""
438        import struct
439        cdef bytes bytesitem
440        # Do a manual and complete check here instead of this easy hack
441        bytesitem = itemp[:self.view.itemsize]
442        try:
443            result = struct.unpack(self.view.format, bytesitem)
444        except struct.error:
445            raise ValueError("Unable to convert item to object")
446        else:
447            if len(self.view.format) == 1:
448                return result[0]
449            return result
450
451    cdef assign_item_from_object(self, char *itemp, object value):
452        """Only used if instantiated manually by the user, or if Cython doesn't
453        know how to convert the type"""
454        import struct
455        cdef char c
456        cdef bytes bytesvalue
457        cdef Py_ssize_t i
458
459        if isinstance(value, tuple):
460            bytesvalue = struct.pack(self.view.format, *value)
461        else:
462            bytesvalue = struct.pack(self.view.format, value)
463
464        for i, c in enumerate(bytesvalue):
465            itemp[i] = c
466
467    @cname('getbuffer')
468    def __getbuffer__(self, Py_buffer *info, int flags):
469        if flags & PyBUF_STRIDES:
470            info.shape = self.view.shape
471        else:
472            info.shape = NULL
473
474        if flags & PyBUF_STRIDES:
475            info.strides = self.view.strides
476        else:
477            info.strides = NULL
478
479        if flags & PyBUF_INDIRECT:
480            info.suboffsets = self.view.suboffsets
481        else:
482            info.suboffsets = NULL
483
484        if flags & PyBUF_FORMAT:
485            info.format = self.view.format
486        else:
487            info.format = NULL
488
489        info.buf = self.view.buf
490        info.ndim = self.view.ndim
491        info.itemsize = self.view.itemsize
492        info.len = self.view.len
493        info.readonly = 0
494        info.obj = self
495
496    __pyx_getbuffer = capsule(<void *> &__pyx_memoryview_getbuffer, "getbuffer(obj, view, flags)")
497
498    # Some properties that have the same sematics as in NumPy
499    property T:
500        @cname('__pyx_memoryview_transpose')
501        def __get__(self):
502            cdef _memoryviewslice result = memoryview_copy(self)
503            transpose_memslice(&result.from_slice)
504            return result
505
506    property base:
507        @cname('__pyx_memoryview__get__base')
508        def __get__(self):
509            return self.obj
510
511    property shape:
512        @cname('__pyx_memoryview_get_shape')
513        def __get__(self):
514            return tuple([self.view.shape[i] for i in xrange(self.view.ndim)])
515
516    property strides:
517        @cname('__pyx_memoryview_get_strides')
518        def __get__(self):
519            if self.view.strides == NULL:
520                # Note: we always ask for strides, so if this is not set it's a bug
521                raise ValueError("Buffer view does not expose strides")
522
523            return tuple([self.view.strides[i] for i in xrange(self.view.ndim)])
524
525    property suboffsets:
526        @cname('__pyx_memoryview_get_suboffsets')
527        def __get__(self):
528            if self.view.suboffsets == NULL:
529                return [-1] * self.view.ndim
530
531            return tuple([self.view.suboffsets[i] for i in xrange(self.view.ndim)])
532
533    property ndim:
534        @cname('__pyx_memoryview_get_ndim')
535        def __get__(self):
536            return self.view.ndim
537
538    property itemsize:
539        @cname('__pyx_memoryview_get_itemsize')
540        def __get__(self):
541            return self.view.itemsize
542
543    property nbytes:
544        @cname('__pyx_memoryview_get_nbytes')
545        def __get__(self):
546            return self.size * self.view.itemsize
547
548    property size:
549        @cname('__pyx_memoryview_get_size')
550        def __get__(self):
551            if self._size is None:
552                result = 1
553
554                for length in self.shape:
555                    result *= length
556
557                self._size = result
558
559            return self._size
560
561    def __len__(self):
562        if self.view.ndim >= 1:
563            return self.view.shape[0]
564
565        return 0
566
567    def __repr__(self):
568        return "<MemoryView of %r at 0x%x>" % (self.base.__class__.__name__,
569                                               id(self))
570
571    def __str__(self):
572        return "<MemoryView of %r object>" % (self.base.__class__.__name__,)
573
574    # Support the same attributes as memoryview slices
575    def is_c_contig(self):
576        cdef {{memviewslice_name}} *mslice
577        cdef {{memviewslice_name}} tmp
578        mslice = get_slice_from_memview(self, &tmp)
579        return slice_is_contig(mslice, 'C', self.view.ndim)
580
581    def is_f_contig(self):
582        cdef {{memviewslice_name}} *mslice
583        cdef {{memviewslice_name}} tmp
584        mslice = get_slice_from_memview(self, &tmp)
585        return slice_is_contig(mslice, 'F', self.view.ndim)
586
587    def copy(self):
588        cdef {{memviewslice_name}} mslice
589        cdef int flags = self.flags & ~PyBUF_F_CONTIGUOUS
590
591        slice_copy(self, &mslice)
592        mslice = slice_copy_contig(&mslice, "c", self.view.ndim,
593                                   self.view.itemsize,
594                                   flags|PyBUF_C_CONTIGUOUS,
595                                   self.dtype_is_object)
596
597        return memoryview_copy_from_slice(self, &mslice)
598
599    def copy_fortran(self):
600        cdef {{memviewslice_name}} src, dst
601        cdef int flags = self.flags & ~PyBUF_C_CONTIGUOUS
602
603        slice_copy(self, &src)
604        dst = slice_copy_contig(&src, "fortran", self.view.ndim,
605                                self.view.itemsize,
606                                flags|PyBUF_F_CONTIGUOUS,
607                                self.dtype_is_object)
608
609        return memoryview_copy_from_slice(self, &dst)
610
611
612@cname('__pyx_memoryview_new')
613cdef memoryview_cwrapper(object o, int flags, bint dtype_is_object, __Pyx_TypeInfo *typeinfo):
614    cdef memoryview result = memoryview(o, flags, dtype_is_object)
615    result.typeinfo = typeinfo
616    return result
617
618@cname('__pyx_memoryview_check')
619cdef inline bint memoryview_check(object o):
620    return isinstance(o, memoryview)
621
622cdef tuple _unellipsify(object index, int ndim):
623    """
624    Replace all ellipses with full slices and fill incomplete indices with
625    full slices.
626    """
627    if not isinstance(index, tuple):
628        tup = (index,)
629    else:
630        tup = index
631
632    result = []
633    have_slices = False
634    seen_ellipsis = False
635    for idx, item in enumerate(tup):
636        if item is Ellipsis:
637            if not seen_ellipsis:
638                result.extend([slice(None)] * (ndim - len(tup) + 1))
639                seen_ellipsis = True
640            else:
641                result.append(slice(None))
642            have_slices = True
643        else:
644            if not isinstance(item, slice) and not PyIndex_Check(item):
645                raise TypeError("Cannot index with type '%s'" % type(item))
646
647            have_slices = have_slices or isinstance(item, slice)
648            result.append(item)
649
650    nslices = ndim - len(result)
651    if nslices:
652        result.extend([slice(None)] * nslices)
653
654    return have_slices or nslices, tuple(result)
655
656cdef assert_direct_dimensions(Py_ssize_t *suboffsets, int ndim):
657    cdef int i
658    for i in range(ndim):
659        if suboffsets[i] >= 0:
660            raise ValueError("Indirect dimensions not supported")
661
662#
663### Slicing a memoryview
664#
665
666@cname('__pyx_memview_slice')
667cdef memoryview memview_slice(memoryview memview, object indices):
668    cdef int new_ndim = 0, suboffset_dim = -1, dim
669    cdef bint negative_step
670    cdef {{memviewslice_name}} src, dst
671    cdef {{memviewslice_name}} *p_src
672
673    # dst is copied by value in memoryview_fromslice -- initialize it
674    # src is never copied
675    memset(&dst, 0, sizeof(dst))
676
677    cdef _memoryviewslice memviewsliceobj
678
679    assert memview.view.ndim > 0
680
681    if isinstance(memview, _memoryviewslice):
682        memviewsliceobj = memview
683        p_src = &memviewsliceobj.from_slice
684    else:
685        slice_copy(memview, &src)
686        p_src = &src
687
688    # Note: don't use variable src at this point
689    # SubNote: we should be able to declare variables in blocks...
690
691    # memoryview_fromslice() will inc our dst slice
692    dst.memview = p_src.memview
693    dst.data = p_src.data
694
695    # Put everything in temps to avoid this bloody warning:
696    # "Argument evaluation order in C function call is undefined and
697    #  may not be as expected"
698    cdef {{memviewslice_name}} *p_dst = &dst
699    cdef int *p_suboffset_dim = &suboffset_dim
700    cdef Py_ssize_t start, stop, step
701    cdef bint have_start, have_stop, have_step
702
703    for dim, index in enumerate(indices):
704        if PyIndex_Check(index):
705            slice_memviewslice(
706                p_dst, p_src.shape[dim], p_src.strides[dim], p_src.suboffsets[dim],
707                dim, new_ndim, p_suboffset_dim,
708                index, 0, 0, # start, stop, step
709                0, 0, 0, # have_{start,stop,step}
710                False)
711        elif index is None:
712            p_dst.shape[new_ndim] = 1
713            p_dst.strides[new_ndim] = 0
714            p_dst.suboffsets[new_ndim] = -1
715            new_ndim += 1
716        else:
717            start = index.start or 0
718            stop = index.stop or 0
719            step = index.step or 0
720
721            have_start = index.start is not None
722            have_stop = index.stop is not None
723            have_step = index.step is not None
724
725            slice_memviewslice(
726                p_dst, p_src.shape[dim], p_src.strides[dim], p_src.suboffsets[dim],
727                dim, new_ndim, p_suboffset_dim,
728                start, stop, step,
729                have_start, have_stop, have_step,
730                True)
731            new_ndim += 1
732
733    if isinstance(memview, _memoryviewslice):
734        return memoryview_fromslice(dst, new_ndim,
735                                    memviewsliceobj.to_object_func,
736                                    memviewsliceobj.to_dtype_func,
737                                    memview.dtype_is_object)
738    else:
739        return memoryview_fromslice(dst, new_ndim, NULL, NULL,
740                                    memview.dtype_is_object)
741
742
743#
744### Slicing in a single dimension of a memoryviewslice
745#
746
747cdef extern from "stdlib.h":
748    void abort() nogil
749    void printf(char *s, ...) nogil
750
751cdef extern from "stdio.h":
752    ctypedef struct FILE
753    FILE *stderr
754    int fputs(char *s, FILE *stream)
755
756cdef extern from "pystate.h":
757    void PyThreadState_Get() nogil
758
759    # These are not actually nogil, but we check for the GIL before calling them
760    void PyErr_SetString(PyObject *type, char *msg) nogil
761    PyObject *PyErr_Format(PyObject *exc, char *msg, ...) nogil
762
763@cname('__pyx_memoryview_slice_memviewslice')
764cdef int slice_memviewslice(
765        {{memviewslice_name}} *dst,
766        Py_ssize_t shape, Py_ssize_t stride, Py_ssize_t suboffset,
767        int dim, int new_ndim, int *suboffset_dim,
768        Py_ssize_t start, Py_ssize_t stop, Py_ssize_t step,
769        int have_start, int have_stop, int have_step,
770        bint is_slice) nogil except -1:
771    """
772    Create a new slice dst given slice src.
773
774    dim             - the current src dimension (indexing will make dimensions
775                                                 disappear)
776    new_dim         - the new dst dimension
777    suboffset_dim   - pointer to a single int initialized to -1 to keep track of
778                      where slicing offsets should be added
779    """
780
781    cdef Py_ssize_t new_shape
782    cdef bint negative_step
783
784    if not is_slice:
785        # index is a normal integer-like index
786        if start < 0:
787            start += shape
788        if not 0 <= start < shape:
789            _err_dim(IndexError, "Index out of bounds (axis %d)", dim)
790    else:
791        # index is a slice
792        negative_step = have_step != 0 and step < 0
793
794        if have_step and step == 0:
795            _err_dim(ValueError, "Step may not be zero (axis %d)", dim)
796
797        # check our bounds and set defaults
798        if have_start:
799            if start < 0:
800                start += shape
801                if start < 0:
802                    start = 0
803            elif start >= shape:
804                if negative_step:
805                    start = shape - 1
806                else:
807                    start = shape
808        else:
809            if negative_step:
810                start = shape - 1
811            else:
812                start = 0
813
814        if have_stop:
815            if stop < 0:
816                stop += shape
817                if stop < 0:
818                    stop = 0
819            elif stop > shape:
820                stop = shape
821        else:
822            if negative_step:
823                stop = -1
824            else:
825                stop = shape
826
827        if not have_step:
828            step = 1
829
830        # len = ceil( (stop - start) / step )
831        with cython.cdivision(True):
832            new_shape = (stop - start) // step
833
834            if (stop - start) - step * new_shape:
835                new_shape += 1
836
837        if new_shape < 0:
838            new_shape = 0
839
840        # shape/strides/suboffsets
841        dst.strides[new_ndim] = stride * step
842        dst.shape[new_ndim] = new_shape
843        dst.suboffsets[new_ndim] = suboffset
844
845    # Add the slicing or idexing offsets to the right suboffset or base data *
846    if suboffset_dim[0] < 0:
847        dst.data += start * stride
848    else:
849        dst.suboffsets[suboffset_dim[0]] += start * stride
850
851    if suboffset >= 0:
852        if not is_slice:
853            if new_ndim == 0:
854                dst.data = (<char **> dst.data)[0] + suboffset
855            else:
856                _err_dim(IndexError, "All dimensions preceding dimension %d "
857                                     "must be indexed and not sliced", dim)
858        else:
859            suboffset_dim[0] = new_ndim
860
861    return 0
862
863#
864### Index a memoryview
865#
866@cname('__pyx_pybuffer_index')
867cdef char *pybuffer_index(Py_buffer *view, char *bufp, Py_ssize_t index,
868                          Py_ssize_t dim) except NULL:
869    cdef Py_ssize_t shape, stride, suboffset = -1
870    cdef Py_ssize_t itemsize = view.itemsize
871    cdef char *resultp
872
873    if view.ndim == 0:
874        shape = view.len / itemsize
875        stride = itemsize
876    else:
877        shape = view.shape[dim]
878        stride = view.strides[dim]
879        if view.suboffsets != NULL:
880            suboffset = view.suboffsets[dim]
881
882    if index < 0:
883        index += view.shape[dim]
884        if index < 0:
885            raise IndexError("Out of bounds on buffer access (axis %d)" % dim)
886
887    if index >= shape:
888        raise IndexError("Out of bounds on buffer access (axis %d)" % dim)
889
890    resultp = bufp + index * stride
891    if suboffset >= 0:
892        resultp = (<char **> resultp)[0] + suboffset
893
894    return resultp
895
896#
897### Transposing a memoryviewslice
898#
899@cname('__pyx_memslice_transpose')
900cdef int transpose_memslice({{memviewslice_name}} *memslice) nogil except 0:
901    cdef int ndim = memslice.memview.view.ndim
902
903    cdef Py_ssize_t *shape = memslice.shape
904    cdef Py_ssize_t *strides = memslice.strides
905
906    # reverse strides and shape
907    cdef int i, j
908    for i in range(ndim / 2):
909        j = ndim - 1 - i
910        strides[i], strides[j] = strides[j], strides[i]
911        shape[i], shape[j] = shape[j], shape[i]
912
913        if memslice.suboffsets[i] >= 0 or memslice.suboffsets[j] >= 0:
914            _err(ValueError, "Cannot transpose memoryview with indirect dimensions")
915
916    return 1
917
918#
919### Creating new memoryview objects from slices and memoryviews
920#
921@cname('__pyx_memoryviewslice')
922cdef class _memoryviewslice(memoryview):
923    "Internal class for passing memoryview slices to Python"
924
925    # We need this to keep our shape/strides/suboffset pointers valid
926    cdef {{memviewslice_name}} from_slice
927    # We need this only to print it's class' name
928    cdef object from_object
929
930    cdef object (*to_object_func)(char *)
931    cdef int (*to_dtype_func)(char *, object) except 0
932
933    def __dealloc__(self):
934        __PYX_XDEC_MEMVIEW(&self.from_slice, 1)
935
936    cdef convert_item_to_object(self, char *itemp):
937        if self.to_object_func != NULL:
938            return self.to_object_func(itemp)
939        else:
940            return memoryview.convert_item_to_object(self, itemp)
941
942    cdef assign_item_from_object(self, char *itemp, object value):
943        if self.to_dtype_func != NULL:
944            self.to_dtype_func(itemp, value)
945        else:
946            memoryview.assign_item_from_object(self, itemp, value)
947
948    property base:
949        @cname('__pyx_memoryviewslice__get__base')
950        def __get__(self):
951            return self.from_object
952
953    __pyx_getbuffer = capsule(<void *> &__pyx_memoryview_getbuffer, "getbuffer(obj, view, flags)")
954
955
956@cname('__pyx_memoryview_fromslice')
957cdef memoryview_fromslice({{memviewslice_name}} memviewslice,
958                          int ndim,
959                          object (*to_object_func)(char *),
960                          int (*to_dtype_func)(char *, object) except 0,
961                          bint dtype_is_object):
962
963    cdef _memoryviewslice result
964    cdef int i
965
966    if <PyObject *> memviewslice.memview == Py_None:
967        return None
968
969    # assert 0 < ndim <= memviewslice.memview.view.ndim, (
970    #                 ndim, memviewslice.memview.view.ndim)
971
972    result = _memoryviewslice(None, 0, dtype_is_object)
973
974    result.from_slice = memviewslice
975    __PYX_INC_MEMVIEW(&memviewslice, 1)
976
977    result.from_object = (<memoryview> memviewslice.memview).base
978    result.typeinfo = memviewslice.memview.typeinfo
979
980    result.view = memviewslice.memview.view
981    result.view.buf = <void *> memviewslice.data
982    result.view.ndim = ndim
983    (<__pyx_buffer *> &result.view).obj = Py_None
984    Py_INCREF(Py_None)
985
986    result.flags = PyBUF_RECORDS
987
988    result.view.shape = <Py_ssize_t *> result.from_slice.shape
989    result.view.strides = <Py_ssize_t *> result.from_slice.strides
990    result.view.suboffsets = <Py_ssize_t *> result.from_slice.suboffsets
991
992    result.view.len = result.view.itemsize
993    for i in range(ndim):
994        result.view.len *= result.view.shape[i]
995
996    result.to_object_func = to_object_func
997    result.to_dtype_func = to_dtype_func
998
999    return result
1000
1001@cname('__pyx_memoryview_get_slice_from_memoryview')
1002cdef {{memviewslice_name}} *get_slice_from_memview(memoryview memview,
1003                                                   {{memviewslice_name}} *mslice):
1004    cdef _memoryviewslice obj
1005    if isinstance(memview, _memoryviewslice):
1006        obj = memview
1007        return &obj.from_slice
1008    else:
1009        slice_copy(memview, mslice)
1010        return mslice
1011
1012@cname('__pyx_memoryview_slice_copy')
1013cdef void slice_copy(memoryview memview, {{memviewslice_name}} *dst):
1014    cdef int dim
1015    cdef (Py_ssize_t*) shape, strides, suboffsets
1016
1017    shape = memview.view.shape
1018    strides = memview.view.strides
1019    suboffsets = memview.view.suboffsets
1020
1021    dst.memview = <__pyx_memoryview *> memview
1022    dst.data = <char *> memview.view.buf
1023
1024    for dim in range(memview.view.ndim):
1025        dst.shape[dim] = shape[dim]
1026        dst.strides[dim] = strides[dim]
1027        if suboffsets == NULL:
1028            dst.suboffsets[dim] = -1
1029        else:
1030            dst.suboffsets[dim] = suboffsets[dim]
1031
1032@cname('__pyx_memoryview_copy_object')
1033cdef memoryview_copy(memoryview memview):
1034    "Create a new memoryview object"
1035    cdef {{memviewslice_name}} memviewslice
1036    slice_copy(memview, &memviewslice)
1037    return memoryview_copy_from_slice(memview, &memviewslice)
1038
1039@cname('__pyx_memoryview_copy_object_from_slice')
1040cdef memoryview_copy_from_slice(memoryview memview, {{memviewslice_name}} *memviewslice):
1041    """
1042    Create a new memoryview object from a given memoryview object and slice.
1043    """
1044    cdef object (*to_object_func)(char *)
1045    cdef int (*to_dtype_func)(char *, object) except 0
1046
1047    if isinstance(memview, _memoryviewslice):
1048        to_object_func = (<_memoryviewslice> memview).to_object_func
1049        to_dtype_func = (<_memoryviewslice> memview).to_dtype_func
1050    else:
1051        to_object_func = NULL
1052        to_dtype_func = NULL
1053
1054    return memoryview_fromslice(memviewslice[0], memview.view.ndim,
1055                                to_object_func, to_dtype_func,
1056                                memview.dtype_is_object)
1057
1058
1059#
1060### Copy the contents of a memoryview slices
1061#
1062cdef Py_ssize_t abs_py_ssize_t(Py_ssize_t arg) nogil:
1063    if arg < 0:
1064        return -arg
1065    else:
1066        return arg
1067
1068@cname('__pyx_get_best_slice_order')
1069cdef char get_best_order({{memviewslice_name}} *mslice, int ndim) nogil:
1070    """
1071    Figure out the best memory access order for a given slice.
1072    """
1073    cdef int i
1074    cdef Py_ssize_t c_stride = 0
1075    cdef Py_ssize_t f_stride = 0
1076
1077    for i in range(ndim - 1, -1, -1):
1078        if mslice.shape[i] > 1:
1079            c_stride = mslice.strides[i]
1080            break
1081
1082    for i in range(ndim):
1083        if mslice.shape[i] > 1:
1084            f_stride = mslice.strides[i]
1085            break
1086
1087    if abs_py_ssize_t(c_stride) <= abs_py_ssize_t(f_stride):
1088        return 'C'
1089    else:
1090        return 'F'
1091
1092@cython.cdivision(True)
1093cdef void _copy_strided_to_strided(char *src_data, Py_ssize_t *src_strides,
1094                                   char *dst_data, Py_ssize_t *dst_strides,
1095                                   Py_ssize_t *src_shape, Py_ssize_t *dst_shape,
1096                                   int ndim, size_t itemsize) nogil:
1097    # Note: src_extent is 1 if we're broadcasting
1098    # dst_extent always >= src_extent as we don't do reductions
1099    cdef Py_ssize_t i
1100    cdef Py_ssize_t src_extent = src_shape[0]
1101    cdef Py_ssize_t dst_extent = dst_shape[0]
1102    cdef Py_ssize_t src_stride = src_strides[0]
1103    cdef Py_ssize_t dst_stride = dst_strides[0]
1104
1105    if ndim == 1:
1106       if (src_stride > 0 and dst_stride > 0 and
1107           <size_t> src_stride == itemsize == <size_t> dst_stride):
1108           memcpy(dst_data, src_data, itemsize * dst_extent)
1109       else:
1110           for i in range(dst_extent):
1111               memcpy(dst_data, src_data, itemsize)
1112               src_data += src_stride
1113               dst_data += dst_stride
1114    else:
1115        for i in range(dst_extent):
1116            _copy_strided_to_strided(src_data, src_strides + 1,
1117                                     dst_data, dst_strides + 1,
1118                                     src_shape + 1, dst_shape + 1,
1119                                     ndim - 1, itemsize)
1120            src_data += src_stride
1121            dst_data += dst_stride
1122
1123cdef void copy_strided_to_strided({{memviewslice_name}} *src,
1124                                  {{memviewslice_name}} *dst,
1125                                  int ndim, size_t itemsize) nogil:
1126    _copy_strided_to_strided(src.data, src.strides, dst.data, dst.strides,
1127                             src.shape, dst.shape, ndim, itemsize)
1128
1129@cname('__pyx_memoryview_slice_get_size')
1130cdef Py_ssize_t slice_get_size({{memviewslice_name}} *src, int ndim) nogil:
1131    "Return the size of the memory occupied by the slice in number of bytes"
1132    cdef int i
1133    cdef Py_ssize_t size = src.memview.view.itemsize
1134
1135    for i in range(ndim):
1136        size *= src.shape[i]
1137
1138    return size
1139
1140@cname('__pyx_fill_contig_strides_array')
1141cdef Py_ssize_t fill_contig_strides_array(
1142                Py_ssize_t *shape, Py_ssize_t *strides, Py_ssize_t stride,
1143                int ndim, char order) nogil:
1144    """
1145    Fill the strides array for a slice with C or F contiguous strides.
1146    This is like PyBuffer_FillContiguousStrides, but compatible with py < 2.6
1147    """
1148    cdef int idx
1149
1150    if order == 'F':
1151        for idx in range(ndim):
1152            strides[idx] = stride
1153            stride = stride * shape[idx]
1154    else:
1155        for idx in range(ndim - 1, -1, -1):
1156            strides[idx] = stride
1157            stride = stride * shape[idx]
1158
1159    return stride
1160
1161@cname('__pyx_memoryview_copy_data_to_temp')
1162cdef void *copy_data_to_temp({{memviewslice_name}} *src,
1163                             {{memviewslice_name}} *tmpslice,
1164                             char order,
1165                             int ndim) nogil except NULL:
1166    """
1167    Copy a direct slice to temporary contiguous memory. The caller should free
1168    the result when done.
1169    """
1170    cdef int i
1171    cdef void *result
1172
1173    cdef size_t itemsize = src.memview.view.itemsize
1174    cdef size_t size = slice_get_size(src, ndim)
1175
1176    result = malloc(size)
1177    if not result:
1178        _err(MemoryError, NULL)
1179
1180    # tmpslice[0] = src
1181    tmpslice.data = <char *> result
1182    tmpslice.memview = src.memview
1183    for i in range(ndim):
1184        tmpslice.shape[i] = src.shape[i]
1185        tmpslice.suboffsets[i] = -1
1186
1187    fill_contig_strides_array(&tmpslice.shape[0], &tmpslice.strides[0], itemsize,
1188                              ndim, order)
1189
1190    # We need to broadcast strides again
1191    for i in range(ndim):
1192        if tmpslice.shape[i] == 1:
1193            tmpslice.strides[i] = 0
1194
1195    if slice_is_contig(src, order, ndim):
1196        memcpy(result, src.data, size)
1197    else:
1198        copy_strided_to_strided(src, tmpslice, ndim, itemsize)
1199
1200    return result
1201
1202# Use 'with gil' functions and avoid 'with gil' blocks, as the code within the blocks
1203# has temporaries that need the GIL to clean up
1204@cname('__pyx_memoryview_err_extents')
1205cdef int _err_extents(int i, Py_ssize_t extent1,
1206                             Py_ssize_t extent2) except -1 with gil:
1207    raise ValueError("got differing extents in dimension %d (got %d and %d)" %
1208                                                        (i, extent1, extent2))
1209
1210@cname('__pyx_memoryview_err_dim')
1211cdef int _err_dim(object error, char *msg, int dim) except -1 with gil:
1212    raise error(msg.decode('ascii') % dim)
1213
1214@cname('__pyx_memoryview_err')
1215cdef int _err(object error, char *msg) except -1 with gil:
1216    if msg != NULL:
1217        raise error(msg.decode('ascii'))
1218    else:
1219        raise error
1220
1221@cname('__pyx_memoryview_copy_contents')
1222cdef int memoryview_copy_contents({{memviewslice_name}} src,
1223                                  {{memviewslice_name}} dst,
1224                                  int src_ndim, int dst_ndim,
1225                                  bint dtype_is_object) nogil except -1:
1226    """
1227    Copy memory from slice src to slice dst.
1228    Check for overlapping memory and verify the shapes.
1229    """
1230    cdef void *tmpdata = NULL
1231    cdef size_t itemsize = src.memview.view.itemsize
1232    cdef int i
1233    cdef char order = get_best_order(&src, src_ndim)
1234    cdef bint broadcasting = False
1235    cdef bint direct_copy = False
1236    cdef {{memviewslice_name}} tmp
1237
1238    if src_ndim < dst_ndim:
1239        broadcast_leading(&src, src_ndim, dst_ndim)
1240    elif dst_ndim < src_ndim:
1241        broadcast_leading(&dst, dst_ndim, src_ndim)
1242
1243    cdef int ndim = max(src_ndim, dst_ndim)
1244
1245    for i in range(ndim):
1246        if src.shape[i] != dst.shape[i]:
1247            if src.shape[i] == 1:
1248                broadcasting = True
1249                src.strides[i] = 0
1250            else:
1251                _err_extents(i, dst.shape[i], src.shape[i])
1252
1253        if src.suboffsets[i] >= 0:
1254            _err_dim(ValueError, "Dimension %d is not direct", i)
1255
1256    if slices_overlap(&src, &dst, ndim, itemsize):
1257        # slices overlap, copy to temp, copy temp to dst
1258        if not slice_is_contig(&src, order, ndim):
1259            order = get_best_order(&dst, ndim)
1260
1261        tmpdata = copy_data_to_temp(&src, &tmp, order, ndim)
1262        src = tmp
1263
1264    if not broadcasting:
1265        # See if both slices have equal contiguity, in that case perform a
1266        # direct copy. This only works when we are not broadcasting.
1267        if slice_is_contig(&src, 'C', ndim):
1268            direct_copy = slice_is_contig(&dst, 'C', ndim)
1269        elif slice_is_contig(&src, 'F', ndim):
1270            direct_copy = slice_is_contig(&dst, 'F', ndim)
1271
1272        if direct_copy:
1273            # Contiguous slices with same order
1274            refcount_copying(&dst, dtype_is_object, ndim, False)
1275            memcpy(dst.data, src.data, slice_get_size(&src, ndim))
1276            refcount_copying(&dst, dtype_is_object, ndim, True)
1277            free(tmpdata)
1278            return 0
1279
1280    if order == 'F' == get_best_order(&dst, ndim):
1281        # see if both slices have Fortran order, transpose them to match our
1282        # C-style indexing order
1283        transpose_memslice(&src)
1284        transpose_memslice(&dst)
1285
1286    refcount_copying(&dst, dtype_is_object, ndim, False)
1287    copy_strided_to_strided(&src, &dst, ndim, itemsize)
1288    refcount_copying(&dst, dtype_is_object, ndim, True)
1289
1290    free(tmpdata)
1291    return 0
1292
1293@cname('__pyx_memoryview_broadcast_leading')
1294cdef void broadcast_leading({{memviewslice_name}} *slice,
1295                            int ndim,
1296                            int ndim_other) nogil:
1297    cdef int i
1298    cdef int offset = ndim_other - ndim
1299
1300    for i in range(ndim - 1, -1, -1):
1301        slice.shape[i + offset] = slice.shape[i]
1302        slice.strides[i + offset] = slice.strides[i]
1303        slice.suboffsets[i + offset] = slice.suboffsets[i]
1304
1305    for i in range(offset):
1306        slice.shape[i] = 1
1307        slice.strides[i] = slice.strides[0]
1308        slice.suboffsets[i] = -1
1309
1310#
1311### Take care of refcounting the objects in slices. Do this seperately from any copying,
1312### to minimize acquiring the GIL
1313#
1314
1315@cname('__pyx_memoryview_refcount_copying')
1316cdef void refcount_copying({{memviewslice_name}} *dst, bint dtype_is_object,
1317                           int ndim, bint inc) nogil:
1318    # incref or decref the objects in the destination slice if the dtype is
1319    # object
1320    if dtype_is_object:
1321        refcount_objects_in_slice_with_gil(dst.data, dst.shape,
1322                                           dst.strides, ndim, inc)
1323
1324@cname('__pyx_memoryview_refcount_objects_in_slice_with_gil')
1325cdef void refcount_objects_in_slice_with_gil(char *data, Py_ssize_t *shape,
1326                                             Py_ssize_t *strides, int ndim,
1327                                             bint inc) with gil:
1328    refcount_objects_in_slice(data, shape, strides, ndim, inc)
1329
1330@cname('__pyx_memoryview_refcount_objects_in_slice')
1331cdef void refcount_objects_in_slice(char *data, Py_ssize_t *shape,
1332                                    Py_ssize_t *strides, int ndim, bint inc):
1333    cdef Py_ssize_t i
1334
1335    for i in range(shape[0]):
1336        if ndim == 1:
1337            if inc:
1338                Py_INCREF((<PyObject **> data)[0])
1339            else:
1340                Py_DECREF((<PyObject **> data)[0])
1341        else:
1342            refcount_objects_in_slice(data, shape + 1, strides + 1,
1343                                      ndim - 1, inc)
1344
1345        data += strides[0]
1346
1347#
1348### Scalar to slice assignment
1349#
1350@cname('__pyx_memoryview_slice_assign_scalar')
1351cdef void slice_assign_scalar({{memviewslice_name}} *dst, int ndim,
1352                              size_t itemsize, void *item,
1353                              bint dtype_is_object) nogil:
1354    refcount_copying(dst, dtype_is_object, ndim, False)
1355    _slice_assign_scalar(dst.data, dst.shape, dst.strides, ndim,
1356                         itemsize, item)
1357    refcount_copying(dst, dtype_is_object, ndim, True)
1358
1359
1360@cname('__pyx_memoryview__slice_assign_scalar')
1361cdef void _slice_assign_scalar(char *data, Py_ssize_t *shape,
1362                              Py_ssize_t *strides, int ndim,
1363                              size_t itemsize, void *item) nogil:
1364    cdef Py_ssize_t i
1365    cdef Py_ssize_t stride = strides[0]
1366    cdef Py_ssize_t extent = shape[0]
1367
1368    if ndim == 1:
1369        for i in range(extent):
1370            memcpy(data, item, itemsize)
1371            data += stride
1372    else:
1373        for i in range(extent):
1374            _slice_assign_scalar(data, shape + 1, strides + 1,
1375                                ndim - 1, itemsize, item)
1376            data += stride
1377
1378
1379############### BufferFormatFromTypeInfo ###############
1380cdef extern from *:
1381    ctypedef struct __Pyx_StructField
1382
1383    cdef enum:
1384        __PYX_BUF_FLAGS_PACKED_STRUCT
1385        __PYX_BUF_FLAGS_INTEGER_COMPLEX
1386
1387    ctypedef struct __Pyx_TypeInfo:
1388      char* name
1389      __Pyx_StructField* fields
1390      size_t size
1391      size_t arraysize[8]
1392      int ndim
1393      char typegroup
1394      char is_unsigned
1395      int flags
1396
1397    ctypedef struct __Pyx_StructField:
1398      __Pyx_TypeInfo* type
1399      char* name
1400      size_t offset
1401
1402    ctypedef struct __Pyx_BufFmt_StackElem:
1403      __Pyx_StructField* field
1404      size_t parent_offset
1405
1406    #ctypedef struct __Pyx_BufFmt_Context:
1407    #  __Pyx_StructField root
1408      __Pyx_BufFmt_StackElem* head
1409
1410    struct __pyx_typeinfo_string:
1411        char string[3]
1412
1413    __pyx_typeinfo_string __Pyx_TypeInfoToFormat(__Pyx_TypeInfo *)
1414
1415
1416@cname('__pyx_format_from_typeinfo')
1417cdef bytes format_from_typeinfo(__Pyx_TypeInfo *type):
1418    cdef __Pyx_StructField *field
1419    cdef __pyx_typeinfo_string fmt
1420    cdef bytes part, result
1421
1422    if type.typegroup == 'S':
1423        assert type.fields != NULL and type.fields.type != NULL
1424
1425        if type.flags & __PYX_BUF_FLAGS_PACKED_STRUCT:
1426            alignment = b'^'
1427        else:
1428            alignment = b''
1429
1430        parts = [b"T{"]
1431        field = type.fields
1432
1433        while field.type:
1434            part = format_from_typeinfo(field.type)
1435            parts.append(part + b':' + field.name + b':')
1436            field += 1
1437
1438        result = alignment.join(parts) + b'}'
1439    else:
1440        fmt = __Pyx_TypeInfoToFormat(type)
1441        if type.arraysize[0]:
1442            extents = [unicode(type.arraysize[i]) for i in range(type.ndim)]
1443            result = (u"(%s)" % u','.join(extents)).encode('ascii') + fmt.string
1444        else:
1445            result = fmt.string
1446
1447    return result
1448