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