MemoryView.pyx 48.2 KB
Newer Older
1
#################### View.MemoryView ####################
2

3 4
# This utility provides cython.array and cython.view.memoryview

5 6 7
from __future__ import absolute_import

cimport cython
8

9
# from cpython cimport ...
10
cdef extern from "Python.h":
11
    int PyIndex_Check(object)
12 13 14 15 16 17 18 19 20 21
    object PyLong_FromVoidPtr(void *)

cdef extern from "pythread.h":
    ctypedef void *PyThread_type_lock

    PyThread_type_lock PyThread_allocate_lock()
    void PyThread_free_lock(PyThread_type_lock)
    int PyThread_acquire_lock(PyThread_type_lock, int mode) nogil
    void PyThread_release_lock(PyThread_type_lock) nogil

22
cdef extern from "<string.h>":
23 24 25 26 27 28 29
    void *memset(void *b, int c, size_t len)

cdef extern from *:
    int __Pyx_GetBuffer(object, Py_buffer *, int) except -1
    void __Pyx_ReleaseBuffer(Py_buffer *)

    ctypedef struct PyObject
30
    ctypedef Py_ssize_t Py_intptr_t
31 32 33
    void Py_INCREF(PyObject *)
    void Py_DECREF(PyObject *)

34 35
    void* PyMem_Malloc(size_t n)
    void PyMem_Free(void *p)
36 37
    void* PyObject_Malloc(size_t n)
    void PyObject_Free(void *p)
38

39 40 41
    cdef struct __pyx_memoryview "__pyx_memoryview_obj":
        Py_buffer view
        PyObject *obj
42
        __Pyx_TypeInfo *typeinfo
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57

    ctypedef struct {{memviewslice_name}}:
        __pyx_memoryview *memview
        char *data
        Py_ssize_t shape[{{max_dims}}]
        Py_ssize_t strides[{{max_dims}}]
        Py_ssize_t suboffsets[{{max_dims}}]

    void __PYX_INC_MEMVIEW({{memviewslice_name}} *memslice, int have_gil)
    void __PYX_XDEC_MEMVIEW({{memviewslice_name}} *memslice, int have_gil)

    ctypedef struct __pyx_buffer "Py_buffer":
        PyObject *obj

    PyObject *Py_None
58 59 60 61 62

    cdef enum:
        PyBUF_C_CONTIGUOUS,
        PyBUF_F_CONTIGUOUS,
        PyBUF_ANY_CONTIGUOUS
63
        PyBUF_FORMAT
64
        PyBUF_WRITABLE
65 66 67
        PyBUF_STRIDES
        PyBUF_INDIRECT
        PyBUF_RECORDS
68
        PyBUF_RECORDS_RO
69

70 71 72
    ctypedef struct __Pyx_TypeInfo:
        pass

73 74 75
    cdef object capsule "__pyx_capsule_create" (void *p, char *sig)
    cdef int __pyx_array_getbuffer(PyObject *obj, Py_buffer view, int flags)
    cdef int __pyx_memoryview_getbuffer(PyObject *obj, Py_buffer view, int flags)
76

77 78 79 80 81 82 83 84
cdef extern from *:
    ctypedef int __pyx_atomic_int
    {{memviewslice_name}} slice_copy_contig "__pyx_memoryview_copy_new_contig"(
                                 __Pyx_memviewslice *from_mvs,
                                 char *mode, int ndim,
                                 size_t sizeof_dtype, int contig_flag,
                                 bint dtype_is_object) nogil except *
    bint slice_is_contig "__pyx_memviewslice_is_contig" (
85
                            {{memviewslice_name}} mvs, char order, int ndim) nogil
86 87 88
    bint slices_overlap "__pyx_slices_overlap" ({{memviewslice_name}} *slice1,
                                                {{memviewslice_name}} *slice2,
                                                int ndim, size_t itemsize) nogil
89

90

91
cdef extern from "<stdlib.h>":
92 93 94 95
    void *malloc(size_t) nogil
    void free(void *) nogil
    void *memcpy(void *dest, void *src, size_t n) nogil

96

97 98


99 100 101 102
#
### cython.array class
#

103 104 105 106 107 108 109 110
@cname("__pyx_array")
cdef class array:

    cdef:
        char *data
        Py_ssize_t len
        char *format
        int ndim
111 112
        Py_ssize_t *_shape
        Py_ssize_t *_strides
113
        Py_ssize_t itemsize
Stefan Behnel's avatar
Stefan Behnel committed
114
        unicode mode  # FIXME: this should have been a simple 'char'
115
        bytes _format
116
        void (*callback_free_data)(void *data)
117
        # cdef object _memview
118
        cdef bint free_data
119
        cdef bint dtype_is_object
120

121
    def __cinit__(array self, tuple shape, Py_ssize_t itemsize, format not None,
122
                  mode="c", bint allocate_buffer=True):
123

124
        cdef int idx
125
        cdef Py_ssize_t i, dim
126 127
        cdef PyObject **p

128
        self.ndim = <int> len(shape)
129 130 131 132 133
        self.itemsize = itemsize

        if not self.ndim:
            raise ValueError("Empty shape tuple for cython.array")

134
        if itemsize <= 0:
135 136
            raise ValueError("itemsize <= 0 for cython.array")

137 138
        if not isinstance(format, bytes):
            format = format.encode('ASCII')
139
        self._format = format  # keep a reference to the byte string
140 141
        self.format = self._format

142
        # use single malloc() for both shape and strides
143
        self._shape = <Py_ssize_t *> PyObject_Malloc(sizeof(Py_ssize_t)*self.ndim*2)
144
        self._strides = self._shape + self.ndim
145

146 147
        if not self._shape:
            raise MemoryError("unable to allocate shape and strides.")
148 149

        # cdef Py_ssize_t dim, stride
150
        for idx, dim in enumerate(shape):
151
            if dim <= 0:
152
                raise ValueError("Invalid shape in axis %d: %d." % (idx, dim))
153
            self._shape[idx] = dim
154

155 156
        cdef char order
        if mode == 'fortran':
157
            order = b'F'
Stefan Behnel's avatar
Stefan Behnel committed
158
            self.mode = u'fortran'
159 160
        elif mode == 'c':
            order = b'C'
161
            self.mode = u'c'
162 163
        else:
            raise ValueError("Invalid mode, expected 'c' or 'fortran', got %s" % mode)
164 165 166

        self.len = fill_contig_strides_array(self._shape, self._strides,
                                             itemsize, self.ndim, order)
167

168
        self.free_data = allocate_buffer
169
        self.dtype_is_object = format == b'O'
170
        if allocate_buffer:
171 172 173
            # use malloc() for backwards compatibility
            # in case external code wants to change the data pointer
            self.data = <char *>malloc(self.len)
174 175 176
            if not self.data:
                raise MemoryError("unable to allocate array data.")

177 178 179 180 181
            if self.dtype_is_object:
                p = <PyObject **> self.data
                for i in range(self.len / itemsize):
                    p[i] = Py_None
                    Py_INCREF(Py_None)
182

183
    @cname('getbuffer')
184 185
    def __getbuffer__(self, Py_buffer *info, int flags):
        cdef int bufmode = -1
186
        if self.mode == u"c":
187
            bufmode = PyBUF_C_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS
188
        elif self.mode == u"fortran":
189 190 191 192 193 194
            bufmode = PyBUF_F_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS
        if not (flags & bufmode):
            raise ValueError("Can only create a buffer that is contiguous in memory.")
        info.buf = self.data
        info.len = self.len
        info.ndim = self.ndim
195 196
        info.shape = self._shape
        info.strides = self._strides
197 198
        info.suboffsets = NULL
        info.itemsize = self.itemsize
199
        info.readonly = 0
200 201 202 203 204 205

        if flags & PyBUF_FORMAT:
            info.format = self.format
        else:
            info.format = NULL

206 207
        info.obj = self

208 209
    __pyx_getbuffer = capsule(<void *> &__pyx_array_getbuffer, "getbuffer(obj, view, flags)")

210
    def __dealloc__(array self):
211 212
        if self.callback_free_data != NULL:
            self.callback_free_data(self.data)
213
        elif self.free_data:
214 215 216
            if self.dtype_is_object:
                refcount_objects_in_slice(self.data, self._shape,
                                          self._strides, self.ndim, False)
217
            free(self.data)
218
        PyObject_Free(self._shape)
219

220 221 222 223 224 225 226 227
    @property
    def memview(self):
        return self.get_memview()

    @cname('get_memview')
    cdef get_memview(self):
        flags =  PyBUF_ANY_CONTIGUOUS|PyBUF_FORMAT|PyBUF_WRITABLE
        return  memoryview(self, flags, self.dtype_is_object)
228

229 230
    def __len__(self):
        return self._shape[0]
231 232 233

    def __getattr__(self, attr):
        return getattr(self.memview, attr)
234

235 236 237 238 239
    def __getitem__(self, item):
        return self.memview[item]

    def __setitem__(self, item, value):
        self.memview[item] = value
240 241


242
@cname("__pyx_array_new")
243 244
cdef array array_cwrapper(tuple shape, Py_ssize_t itemsize, char *format,
                          char *mode, char *buf):
245
    cdef array result
246

247 248 249
    if buf == NULL:
        result = array(shape, itemsize, format, mode.decode('ASCII'))
    else:
250 251
        result = array(shape, itemsize, format, mode.decode('ASCII'),
                       allocate_buffer=False)
252 253 254
        result.data = buf

    return result
255

256

257 258 259
#
### Memoryview constants and cython.view.memoryview class
#
260

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
# Disable generic_contiguous, as it makes trouble verifying contiguity:
#   - 'contiguous' or '::1' means the dimension is contiguous with dtype
#   - 'indirect_contiguous' means a contiguous list of pointers
#   - dtype contiguous must be contiguous in the first or last dimension
#     from the start, or from the dimension following the last indirect dimension
#
#   e.g.
#           int[::indirect_contiguous, ::contiguous, :]
#
#   is valid (list of pointers to 2d fortran-contiguous array), but
#
#           int[::generic_contiguous, ::contiguous, :]
#
#   would mean you'd have assert dimension 0 to be indirect (and pointer contiguous) at runtime.
#   So it doesn't bring any performance benefit, and it's only confusing.

277 278 279 280 281 282 283 284
@cname('__pyx_MemviewEnum')
cdef class Enum(object):
    cdef object name
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return self.name

285 286 287
cdef generic = Enum("<strided and direct or indirect>")
cdef strided = Enum("<strided and direct>") # default
cdef indirect = Enum("<strided and indirect>")
288 289
# Disable generic_contiguous, as it is a troublemaker
#cdef generic_contiguous = Enum("<contiguous and direct or indirect>")
290 291
cdef contiguous = Enum("<contiguous and direct>")
cdef indirect_contiguous = Enum("<contiguous and indirect>")
292

293
# 'follow' is implied when the first or last axis is ::1
294

295 296 297 298 299 300 301 302 303 304 305 306 307 308 309

@cname('__pyx_align_pointer')
cdef void *align_pointer(void *memory, size_t alignment) nogil:
    "Align pointer memory on a given boundary"
    cdef Py_intptr_t aligned_p = <Py_intptr_t> memory
    cdef size_t offset

    with cython.cdivision(True):
        offset = aligned_p % alignment

    if offset > 0:
        aligned_p += alignment - offset

    return <void *> aligned_p

310 311 312 313 314

# pre-allocate thread locks for reuse
## note that this could be implemented in a more beautiful way in "normal" Cython,
## but this code gets merged into the user module and not everything works there.
DEF THREAD_LOCKS_PREALLOCATED = 8
315 316
cdef int __pyx_memoryview_thread_locks_used = 0
cdef PyThread_type_lock[THREAD_LOCKS_PREALLOCATED] __pyx_memoryview_thread_locks = [
317 318 319 320 321 322 323 324 325 326 327
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
]


328 329 330 331
@cname('__pyx_memoryview')
cdef class memoryview(object):

    cdef object obj
332 333
    cdef object _size
    cdef object _array_interface
334
    cdef PyThread_type_lock lock
335 336 337 338
    # the following array will contain a single __pyx_atomic int with
    # suitable alignment
    cdef __pyx_atomic_int acquisition_count[2]
    cdef __pyx_atomic_int *acquisition_count_aligned_p
339
    cdef Py_buffer view
340
    cdef int flags
341
    cdef bint dtype_is_object
342
    cdef __Pyx_TypeInfo *typeinfo
343

344
    def __cinit__(memoryview self, object obj, int flags, bint dtype_is_object=False):
345
        self.obj = obj
346
        self.flags = flags
347 348
        if type(self) is memoryview or obj is not None:
            __Pyx_GetBuffer(obj, &self.view, flags)
Mark Florisson's avatar
Mark Florisson committed
349 350
            if <PyObject *> self.view.obj == NULL:
                (<__pyx_buffer *> &self.view).obj = Py_None
351
                Py_INCREF(Py_None)
352

353 354 355 356
        global __pyx_memoryview_thread_locks_used
        if __pyx_memoryview_thread_locks_used < THREAD_LOCKS_PREALLOCATED:
            self.lock = __pyx_memoryview_thread_locks[__pyx_memoryview_thread_locks_used]
            __pyx_memoryview_thread_locks_used += 1
357 358 359 360
        if self.lock is NULL:
            self.lock = PyThread_allocate_lock()
            if self.lock is NULL:
                raise MemoryError
361

362
        if flags & PyBUF_FORMAT:
363
            self.dtype_is_object = (self.view.format[0] == b'O' and self.view.format[1] == b'\0')
364 365 366
        else:
            self.dtype_is_object = dtype_is_object

367 368
        self.acquisition_count_aligned_p = <__pyx_atomic_int *> align_pointer(
                  <void *> &self.acquisition_count[0], sizeof(__pyx_atomic_int))
369
        self.typeinfo = NULL
370

371
    def __dealloc__(memoryview self):
372 373 374
        if self.obj is not None:
            __Pyx_ReleaseBuffer(&self.view)

375
        cdef int i
376
        global __pyx_memoryview_thread_locks_used
377
        if self.lock != NULL:
378 379 380 381 382 383
            for i in range(__pyx_memoryview_thread_locks_used):
                if __pyx_memoryview_thread_locks[i] is self.lock:
                    __pyx_memoryview_thread_locks_used -= 1
                    if i != __pyx_memoryview_thread_locks_used:
                        __pyx_memoryview_thread_locks[i], __pyx_memoryview_thread_locks[__pyx_memoryview_thread_locks_used] = (
                            __pyx_memoryview_thread_locks[__pyx_memoryview_thread_locks_used], __pyx_memoryview_thread_locks[i])
384 385 386
                    break
            else:
                PyThread_free_lock(self.lock)
387

388 389
    cdef char *get_item_pointer(memoryview self, object index) except NULL:
        cdef Py_ssize_t dim
390
        cdef char *itemp = <char *> self.view.buf
391

392 393
        for dim, idx in enumerate(index):
            itemp = pybuffer_index(&self.view, itemp, idx, dim)
394 395

        return itemp
396

397
    #@cname('__pyx_memoryview_getitem')
398
    def __getitem__(memoryview self, object index):
399 400 401
        if index is Ellipsis:
            return self

402
        have_slices, indices = _unellipsify(index, self.view.ndim)
403 404 405

        cdef char *itemp
        if have_slices:
406
            return memview_slice(self, indices)
407
        else:
408
            itemp = self.get_item_pointer(indices)
409
            return self.convert_item_to_object(itemp)
410 411

    def __setitem__(memoryview self, object index, object value):
412 413 414
        if self.view.readonly:
            raise TypeError("Cannot assign to read-only memoryview")

415
        have_slices, index = _unellipsify(index, self.view.ndim)
416

417
        if have_slices:
418 419
            obj = self.is_slice(value)
            if obj:
420
                self.setitem_slice_assignment(self[index], obj)
421
            else:
422
                self.setitem_slice_assign_scalar(self[index], value)
423 424 425 426 427 428
        else:
            self.setitem_indexed(index, value)

    cdef is_slice(self, obj):
        if not isinstance(obj, memoryview):
            try:
429 430
                obj = memoryview(obj, self.flags|PyBUF_ANY_CONTIGUOUS,
                                 self.dtype_is_object)
431 432 433 434 435
            except TypeError:
                return None

        return obj

436
    cdef setitem_slice_assignment(self, dst, src):
437 438 439
        cdef {{memviewslice_name}} dst_slice
        cdef {{memviewslice_name}} src_slice

440 441
        memoryview_copy_contents(get_slice_from_memview(src, &src_slice)[0],
                                 get_slice_from_memview(dst, &dst_slice)[0],
442
                                 src.ndim, dst.ndim, self.dtype_is_object)
443

444
    cdef setitem_slice_assign_scalar(self, memoryview dst, value):
445 446 447 448
        cdef int array[128]
        cdef void *tmp = NULL
        cdef void *item

Robert Bradshaw's avatar
Robert Bradshaw committed
449 450
        cdef {{memviewslice_name}} *dst_slice
        cdef {{memviewslice_name}} tmp_slice
451 452
        dst_slice = get_slice_from_memview(dst, &tmp_slice)

453
        if <size_t>self.view.itemsize > sizeof(array):
454
            tmp = PyMem_Malloc(self.view.itemsize)
455 456 457 458 459 460
            if tmp == NULL:
                raise MemoryError
            item = tmp
        else:
            item = <void *> array

461 462 463 464
        try:
            if self.dtype_is_object:
                (<PyObject **> item)[0] = <PyObject *> value
            else:
465
                self.assign_item_from_object(<char *> item, value)
466 467 468 469 470 471 472 473

            # It would be easy to support indirect dimensions, but it's easier
            # to disallow :)
            if self.view.suboffsets != NULL:
                assert_direct_dimensions(self.view.suboffsets, self.view.ndim)
            slice_assign_scalar(dst_slice, dst.view.ndim, self.view.itemsize,
                                item, self.dtype_is_object)
        finally:
474
            PyMem_Free(tmp)
475 476

    cdef setitem_indexed(self, index, value):
477 478 479 480 481 482 483 484
        cdef char *itemp = self.get_item_pointer(index)
        self.assign_item_from_object(itemp, value)

    cdef convert_item_to_object(self, char *itemp):
        """Only used if instantiated manually by the user, or if Cython doesn't
        know how to convert the type"""
        import struct
        cdef bytes bytesitem
485 486
        # Do a manual and complete check here instead of this easy hack
        bytesitem = itemp[:self.view.itemsize]
487 488 489 490 491 492 493 494
        try:
            result = struct.unpack(self.view.format, bytesitem)
        except struct.error:
            raise ValueError("Unable to convert item to object")
        else:
            if len(self.view.format) == 1:
                return result[0]
            return result
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510

    cdef assign_item_from_object(self, char *itemp, object value):
        """Only used if instantiated manually by the user, or if Cython doesn't
        know how to convert the type"""
        import struct
        cdef char c
        cdef bytes bytesvalue
        cdef Py_ssize_t i

        if isinstance(value, tuple):
            bytesvalue = struct.pack(self.view.format, *value)
        else:
            bytesvalue = struct.pack(self.view.format, value)

        for i, c in enumerate(bytesvalue):
            itemp[i] = c
511

512
    @cname('getbuffer')
513
    def __getbuffer__(self, Py_buffer *info, int flags):
514 515 516
        if flags & PyBUF_WRITABLE and self.view.readonly:
            raise ValueError("Cannot create writable memory view from read-only memoryview")

517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
        if flags & PyBUF_STRIDES:
            info.shape = self.view.shape
        else:
            info.shape = NULL

        if flags & PyBUF_STRIDES:
            info.strides = self.view.strides
        else:
            info.strides = NULL

        if flags & PyBUF_INDIRECT:
            info.suboffsets = self.view.suboffsets
        else:
            info.suboffsets = NULL

        if flags & PyBUF_FORMAT:
            info.format = self.view.format
        else:
            info.format = NULL

        info.buf = self.view.buf
        info.ndim = self.view.ndim
        info.itemsize = self.view.itemsize
        info.len = self.view.len
541
        info.readonly = self.view.readonly
542 543
        info.obj = self

544 545
    __pyx_getbuffer = capsule(<void *> &__pyx_memoryview_getbuffer, "getbuffer(obj, view, flags)")

luz.paz's avatar
luz.paz committed
546
    # Some properties that have the same semantics as in NumPy
547 548 549 550 551
    @property
    def T(self):
        cdef _memoryviewslice result = memoryview_copy(self)
        transpose_memslice(&result.from_slice)
        return result
552

553 554 555
    @property
    def base(self):
        return self.obj
556

557 558 559
    @property
    def shape(self):
        return tuple([length for length in self.view.shape[:self.view.ndim]])
560

561 562 563 564 565
    @property
    def strides(self):
        if self.view.strides == NULL:
            # Note: we always ask for strides, so if this is not set it's a bug
            raise ValueError("Buffer view does not expose strides")
566

567
        return tuple([stride for stride in self.view.strides[:self.view.ndim]])
568

569 570 571 572
    @property
    def suboffsets(self):
        if self.view.suboffsets == NULL:
            return (-1,) * self.view.ndim
573

574
        return tuple([suboffset for suboffset in self.view.suboffsets[:self.view.ndim]])
575

576 577 578
    @property
    def ndim(self):
        return self.view.ndim
579

580 581 582
    @property
    def itemsize(self):
        return self.view.itemsize
583

584 585 586
    @property
    def nbytes(self):
        return self.size * self.view.itemsize
587

588 589 590 591
    @property
    def size(self):
        if self._size is None:
            result = 1
592

593 594
            for length in self.view.shape[:self.view.ndim]:
                result *= length
595

596
            self._size = result
597

598
        return self._size
599 600 601 602 603 604 605

    def __len__(self):
        if self.view.ndim >= 1:
            return self.view.shape[0]

        return 0

606
    def __repr__(self):
607 608
        return "<MemoryView of %r at 0x%x>" % (self.base.__class__.__name__,
                                               id(self))
609

610
    def __str__(self):
611
        return "<MemoryView of %r object>" % (self.base.__class__.__name__,)
612

613 614
    # Support the same attributes as memoryview slices
    def is_c_contig(self):
Robert Bradshaw's avatar
Robert Bradshaw committed
615 616
        cdef {{memviewslice_name}} *mslice
        cdef {{memviewslice_name}} tmp
617
        mslice = get_slice_from_memview(self, &tmp)
618
        return slice_is_contig(mslice[0], 'C', self.view.ndim)
619 620

    def is_f_contig(self):
Robert Bradshaw's avatar
Robert Bradshaw committed
621 622
        cdef {{memviewslice_name}} *mslice
        cdef {{memviewslice_name}} tmp
623
        mslice = get_slice_from_memview(self, &tmp)
624
        return slice_is_contig(mslice[0], 'F', self.view.ndim)
625 626 627 628

    def copy(self):
        cdef {{memviewslice_name}} mslice
        cdef int flags = self.flags & ~PyBUF_F_CONTIGUOUS
629

630 631 632
        slice_copy(self, &mslice)
        mslice = slice_copy_contig(&mslice, "c", self.view.ndim,
                                   self.view.itemsize,
633 634 635
                                   flags|PyBUF_C_CONTIGUOUS,
                                   self.dtype_is_object)

636 637 638 639 640
        return memoryview_copy_from_slice(self, &mslice)

    def copy_fortran(self):
        cdef {{memviewslice_name}} src, dst
        cdef int flags = self.flags & ~PyBUF_C_CONTIGUOUS
641

642 643 644
        slice_copy(self, &src)
        dst = slice_copy_contig(&src, "fortran", self.view.ndim,
                                self.view.itemsize,
645 646 647
                                flags|PyBUF_F_CONTIGUOUS,
                                self.dtype_is_object)

648 649
        return memoryview_copy_from_slice(self, &dst)

650 651

@cname('__pyx_memoryview_new')
652 653 654 655 656 657
cdef memoryview_cwrapper(object o, int flags, bint dtype_is_object, __Pyx_TypeInfo *typeinfo):
    cdef memoryview result = memoryview(o, flags, dtype_is_object)
    result.typeinfo = typeinfo
    return result

@cname('__pyx_memoryview_check')
658
cdef inline bint memoryview_check(object o):
659
    return isinstance(o, memoryview)
660

661
cdef tuple _unellipsify(object index, int ndim):
662 663 664 665
    """
    Replace all ellipses with full slices and fill incomplete indices with
    full slices.
    """
666 667 668 669 670 671 672 673 674 675 676
    if not isinstance(index, tuple):
        tup = (index,)
    else:
        tup = index

    result = []
    have_slices = False
    seen_ellipsis = False
    for idx, item in enumerate(tup):
        if item is Ellipsis:
            if not seen_ellipsis:
677
                result.extend([slice(None)] * (ndim - len(tup) + 1))
678
                seen_ellipsis = True
679 680 681 682
            else:
                result.append(slice(None))
            have_slices = True
        else:
683 684 685
            if not isinstance(item, slice) and not PyIndex_Check(item):
                raise TypeError("Cannot index with type '%s'" % type(item))

686
            have_slices = have_slices or isinstance(item, slice)
687 688
            result.append(item)

689 690 691 692
    nslices = ndim - len(result)
    if nslices:
        result.extend([slice(None)] * nslices)

693
    return have_slices or nslices, tuple(result)
694

695
cdef assert_direct_dimensions(Py_ssize_t *suboffsets, int ndim):
696 697
    for suboffset in suboffsets[:ndim]:
        if suboffset >= 0:
698 699
            raise ValueError("Indirect dimensions not supported")

700 701 702
#
### Slicing a memoryview
#
703

704 705 706
@cname('__pyx_memview_slice')
cdef memoryview memview_slice(memoryview memview, object indices):
    cdef int new_ndim = 0, suboffset_dim = -1, dim
707
    cdef bint negative_step
Mark Florisson's avatar
Mark Florisson committed
708
    cdef {{memviewslice_name}} src, dst
709
    cdef {{memviewslice_name}} *p_src
710

Mark Florisson's avatar
Mark Florisson committed
711 712 713 714
    # dst is copied by value in memoryview_fromslice -- initialize it
    # src is never copied
    memset(&dst, 0, sizeof(dst))

715
    cdef _memoryviewslice memviewsliceobj
716

717 718 719 720 721 722
    assert memview.view.ndim > 0

    if isinstance(memview, _memoryviewslice):
        memviewsliceobj = memview
        p_src = &memviewsliceobj.from_slice
    else:
723
        slice_copy(memview, &src)
724 725 726 727 728 729 730 731 732
        p_src = &src

    # Note: don't use variable src at this point
    # SubNote: we should be able to declare variables in blocks...

    # memoryview_fromslice() will inc our dst slice
    dst.memview = p_src.memview
    dst.data = p_src.data

733 734 735 736 737 738 739 740
    # Put everything in temps to avoid this bloody warning:
    # "Argument evaluation order in C function call is undefined and
    #  may not be as expected"
    cdef {{memviewslice_name}} *p_dst = &dst
    cdef int *p_suboffset_dim = &suboffset_dim
    cdef Py_ssize_t start, stop, step
    cdef bint have_start, have_stop, have_step

741
    for dim, index in enumerate(indices):
742
        if PyIndex_Check(index):
743 744 745 746 747 748
            slice_memviewslice(
                p_dst, p_src.shape[dim], p_src.strides[dim], p_src.suboffsets[dim],
                dim, new_ndim, p_suboffset_dim,
                index, 0, 0, # start, stop, step
                0, 0, 0, # have_{start,stop,step}
                False)
749 750 751 752 753
        elif index is None:
            p_dst.shape[new_ndim] = 1
            p_dst.strides[new_ndim] = 0
            p_dst.suboffsets[new_ndim] = -1
            new_ndim += 1
754
        else:
755 756 757 758 759 760 761 762
            start = index.start or 0
            stop = index.stop or 0
            step = index.step or 0

            have_start = index.start is not None
            have_stop = index.stop is not None
            have_step = index.step is not None

763 764 765 766 767 768
            slice_memviewslice(
                p_dst, p_src.shape[dim], p_src.strides[dim], p_src.suboffsets[dim],
                dim, new_ndim, p_suboffset_dim,
                start, stop, step,
                have_start, have_stop, have_step,
                True)
769 770
            new_ndim += 1

771
    if isinstance(memview, _memoryviewslice):
772
        return memoryview_fromslice(dst, new_ndim,
773
                                    memviewsliceobj.to_object_func,
774 775
                                    memviewsliceobj.to_dtype_func,
                                    memview.dtype_is_object)
776
    else:
777
        return memoryview_fromslice(dst, new_ndim, NULL, NULL,
778
                                    memview.dtype_is_object)
779

780

781 782 783
#
### Slicing in a single dimension of a memoryviewslice
#
784

785
cdef extern from "<stdlib.h>":
786
    void abort() nogil
Mark Florisson's avatar
Mark Florisson committed
787
    void printf(char *s, ...) nogil
788

789
cdef extern from "<stdio.h>":
790 791 792 793 794 795 796 797 798 799 800 801
    ctypedef struct FILE
    FILE *stderr
    int fputs(char *s, FILE *stream)

cdef extern from "pystate.h":
    void PyThreadState_Get() nogil

    # These are not actually nogil, but we check for the GIL before calling them
    void PyErr_SetString(PyObject *type, char *msg) nogil
    PyObject *PyErr_Format(PyObject *exc, char *msg, ...) nogil

@cname('__pyx_memoryview_slice_memviewslice')
802 803 804 805 806 807 808
cdef int slice_memviewslice(
        {{memviewslice_name}} *dst,
        Py_ssize_t shape, Py_ssize_t stride, Py_ssize_t suboffset,
        int dim, int new_ndim, int *suboffset_dim,
        Py_ssize_t start, Py_ssize_t stop, Py_ssize_t step,
        int have_start, int have_stop, int have_step,
        bint is_slice) nogil except -1:
809 810 811 812 813 814 815 816 817
    """
    Create a new slice dst given slice src.

    dim             - the current src dimension (indexing will make dimensions
                                                 disappear)
    new_dim         - the new dst dimension
    suboffset_dim   - pointer to a single int initialized to -1 to keep track of
                      where slicing offsets should be added
    """
818

819 820
    cdef Py_ssize_t new_shape
    cdef bint negative_step
821 822 823 824 825 826

    if not is_slice:
        # index is a normal integer-like index
        if start < 0:
            start += shape
        if not 0 <= start < shape:
827
            _err_dim(IndexError, "Index out of bounds (axis %d)", dim)
828 829 830 831 832
    else:
        # index is a slice
        negative_step = have_step != 0 and step < 0

        if have_step and step == 0:
833
            _err_dim(ValueError, "Step may not be zero (axis %d)", dim)
834 835 836

        # check our bounds and set defaults
        if have_start:
837 838 839 840 841
            if start < 0:
                start += shape
                if start < 0:
                    start = 0
            elif start >= shape:
842 843 844 845 846 847
                if negative_step:
                    start = shape - 1
                else:
                    start = shape
        else:
            if negative_step:
848
                start = shape - 1
849 850
            else:
                start = 0
851

852
        if have_stop:
853 854 855 856 857 858
            if stop < 0:
                stop += shape
                if stop < 0:
                    stop = 0
            elif stop > shape:
                stop = shape
859 860 861 862 863
        else:
            if negative_step:
                stop = -1
            else:
                stop = shape
864

865 866
        if not have_step:
            step = 1
867

868 869 870
        # len = ceil( (stop - start) / step )
        with cython.cdivision(True):
            new_shape = (stop - start) // step
871

872
            if (stop - start) - step * new_shape:
873
                new_shape += 1
874

875 876
        if new_shape < 0:
            new_shape = 0
877

878 879 880 881
        # shape/strides/suboffsets
        dst.strides[new_ndim] = stride * step
        dst.shape[new_ndim] = new_shape
        dst.suboffsets[new_ndim] = suboffset
882

883 884 885
    # Add the slicing or idexing offsets to the right suboffset or base data *
    if suboffset_dim[0] < 0:
        dst.data += start * stride
886
    else:
887 888 889 890
        dst.suboffsets[suboffset_dim[0]] += start * stride

    if suboffset >= 0:
        if not is_slice:
891 892 893
            if new_ndim == 0:
                dst.data = (<char **> dst.data)[0] + suboffset
            else:
894 895
                _err_dim(IndexError, "All dimensions preceding dimension %d "
                                     "must be indexed and not sliced", dim)
896 897
        else:
            suboffset_dim[0] = new_ndim
898

899
    return 0
900 901 902 903

#
### Index a memoryview
#
904
@cname('__pyx_pybuffer_index')
905
cdef char *pybuffer_index(Py_buffer *view, char *bufp, Py_ssize_t index,
906
                          Py_ssize_t dim) except NULL:
907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
    cdef Py_ssize_t shape, stride, suboffset = -1
    cdef Py_ssize_t itemsize = view.itemsize
    cdef char *resultp

    if view.ndim == 0:
        shape = view.len / itemsize
        stride = itemsize
    else:
        shape = view.shape[dim]
        stride = view.strides[dim]
        if view.suboffsets != NULL:
            suboffset = view.suboffsets[dim]

    if index < 0:
        index += view.shape[dim]
        if index < 0:
            raise IndexError("Out of bounds on buffer access (axis %d)" % dim)

    if index >= shape:
        raise IndexError("Out of bounds on buffer access (axis %d)" % dim)

    resultp = bufp + index * stride
    if suboffset >= 0:
        resultp = (<char **> resultp)[0] + suboffset

    return resultp

934 935 936 937
#
### Transposing a memoryviewslice
#
@cname('__pyx_memslice_transpose')
Mark Florisson's avatar
Mark Florisson committed
938
cdef int transpose_memslice({{memviewslice_name}} *memslice) nogil except 0:
939 940 941 942 943 944 945 946 947 948 949 950 951
    cdef int ndim = memslice.memview.view.ndim

    cdef Py_ssize_t *shape = memslice.shape
    cdef Py_ssize_t *strides = memslice.strides

    # reverse strides and shape
    cdef int i, j
    for i in range(ndim / 2):
        j = ndim - 1 - i
        strides[i], strides[j] = strides[j], strides[i]
        shape[i], shape[j] = shape[j], shape[i]

        if memslice.suboffsets[i] >= 0 or memslice.suboffsets[j] >= 0:
952
            _err(ValueError, "Cannot transpose memoryview with indirect dimensions")
953 954 955

    return 1

956 957 958
#
### Creating new memoryview objects from slices and memoryviews
#
959 960
@cname('__pyx_memoryviewslice')
cdef class _memoryviewslice(memoryview):
961
    "Internal class for passing memoryview slices to Python"
962 963 964

    # We need this to keep our shape/strides/suboffset pointers valid
    cdef {{memviewslice_name}} from_slice
965
    # We need this only to print it's class' name
966
    cdef object from_object
967

968 969 970
    cdef object (*to_object_func)(char *)
    cdef int (*to_dtype_func)(char *, object) except 0

971
    def __dealloc__(self):
972
        __PYX_XDEC_MEMVIEW(&self.from_slice, 1)
973

974 975
    cdef convert_item_to_object(self, char *itemp):
        if self.to_object_func != NULL:
976
            return self.to_object_func(itemp)
977
        else:
978
            return memoryview.convert_item_to_object(self, itemp)
979 980 981 982 983 984 985

    cdef assign_item_from_object(self, char *itemp, object value):
        if self.to_dtype_func != NULL:
            self.to_dtype_func(itemp, value)
        else:
            memoryview.assign_item_from_object(self, itemp, value)

986 987 988
    @property
    def base(self):
        return self.from_object
989

990 991 992
    __pyx_getbuffer = capsule(<void *> &__pyx_memoryview_getbuffer, "getbuffer(obj, view, flags)")


993
@cname('__pyx_memoryview_fromslice')
994
cdef memoryview_fromslice({{memviewslice_name}} memviewslice,
995 996
                          int ndim,
                          object (*to_object_func)(char *),
997 998
                          int (*to_dtype_func)(char *, object) except 0,
                          bint dtype_is_object):
999

1000 1001
    cdef _memoryviewslice result

1002 1003 1004
    if <PyObject *> memviewslice.memview == Py_None:
        return None

1005 1006
    # assert 0 < ndim <= memviewslice.memview.view.ndim, (
    #                 ndim, memviewslice.memview.view.ndim)
1007

1008
    result = _memoryviewslice(None, 0, dtype_is_object)
1009

1010 1011
    result.from_slice = memviewslice
    __PYX_INC_MEMVIEW(&memviewslice, 1)
1012

1013
    result.from_object = (<memoryview> memviewslice.memview).base
1014
    result.typeinfo = memviewslice.memview.typeinfo
1015

1016
    result.view = memviewslice.memview.view
1017
    result.view.buf = <void *> memviewslice.data
1018
    result.view.ndim = ndim
Mark Florisson's avatar
Mark Florisson committed
1019
    (<__pyx_buffer *> &result.view).obj = Py_None
1020
    Py_INCREF(Py_None)
1021

1022 1023 1024 1025
    if (<memoryview>memviewslice.memview).flags & PyBUF_WRITABLE:
        result.flags = PyBUF_RECORDS
    else:
        result.flags = PyBUF_RECORDS_RO
1026

1027 1028
    result.view.shape = <Py_ssize_t *> result.from_slice.shape
    result.view.strides = <Py_ssize_t *> result.from_slice.strides
1029 1030

    # only set suboffsets if actually used, otherwise set to NULL to improve compatibility
1031
    result.view.suboffsets = NULL
1032 1033
    for suboffset in result.from_slice.suboffsets[:ndim]:
        if suboffset >= 0:
1034 1035
            result.view.suboffsets = <Py_ssize_t *> result.from_slice.suboffsets
            break
1036 1037

    result.view.len = result.view.itemsize
Stefan Behnel's avatar
Stefan Behnel committed
1038 1039
    for length in result.view.shape[:ndim]:
        result.view.len *= length
1040

1041
    result.to_object_func = to_object_func
1042
    result.to_dtype_func = to_dtype_func
1043 1044 1045

    return result

1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056
@cname('__pyx_memoryview_get_slice_from_memoryview')
cdef {{memviewslice_name}} *get_slice_from_memview(memoryview memview,
                                                   {{memviewslice_name}} *mslice):
    cdef _memoryviewslice obj
    if isinstance(memview, _memoryviewslice):
        obj = memview
        return &obj.from_slice
    else:
        slice_copy(memview, mslice)
        return mslice

1057 1058
@cname('__pyx_memoryview_slice_copy')
cdef void slice_copy(memoryview memview, {{memviewslice_name}} *dst):
1059
    cdef int dim
Robert Bradshaw's avatar
Robert Bradshaw committed
1060
    cdef (Py_ssize_t*) shape, strides, suboffsets
1061 1062 1063 1064

    shape = memview.view.shape
    strides = memview.view.strides
    suboffsets = memview.view.suboffsets
1065 1066 1067 1068 1069

    dst.memview = <__pyx_memoryview *> memview
    dst.data = <char *> memview.view.buf

    for dim in range(memview.view.ndim):
1070 1071
        dst.shape[dim] = shape[dim]
        dst.strides[dim] = strides[dim]
1072
        dst.suboffsets[dim] = suboffsets[dim] if suboffsets else -1
1073

1074
@cname('__pyx_memoryview_copy_object')
1075
cdef memoryview_copy(memoryview memview):
1076
    "Create a new memoryview object"
1077
    cdef {{memviewslice_name}} memviewslice
1078 1079 1080 1081 1082 1083 1084 1085
    slice_copy(memview, &memviewslice)
    return memoryview_copy_from_slice(memview, &memviewslice)

@cname('__pyx_memoryview_copy_object_from_slice')
cdef memoryview_copy_from_slice(memoryview memview, {{memviewslice_name}} *memviewslice):
    """
    Create a new memoryview object from a given memoryview object and slice.
    """
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
    cdef object (*to_object_func)(char *)
    cdef int (*to_dtype_func)(char *, object) except 0

    if isinstance(memview, _memoryviewslice):
        to_object_func = (<_memoryviewslice> memview).to_object_func
        to_dtype_func = (<_memoryviewslice> memview).to_dtype_func
    else:
        to_object_func = NULL
        to_dtype_func = NULL

1096
    return memoryview_fromslice(memviewslice[0], memview.view.ndim,
1097 1098
                                to_object_func, to_dtype_func,
                                memview.dtype_is_object)
1099

1100

1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
#
### Copy the contents of a memoryview slices
#
cdef Py_ssize_t abs_py_ssize_t(Py_ssize_t arg) nogil:
    if arg < 0:
        return -arg
    else:
        return arg

@cname('__pyx_get_best_slice_order')
cdef char get_best_order({{memviewslice_name}} *mslice, int ndim) nogil:
    """
    Figure out the best memory access order for a given slice.
    """
    cdef int i
    cdef Py_ssize_t c_stride = 0
    cdef Py_ssize_t f_stride = 0

1119
    for i in range(ndim - 1, -1, -1):
1120 1121
        if mslice.shape[i] > 1:
            c_stride = mslice.strides[i]
1122
            break
1123 1124 1125 1126

    for i in range(ndim):
        if mslice.shape[i] > 1:
            f_stride = mslice.strides[i]
1127
            break
1128 1129 1130 1131 1132 1133

    if abs_py_ssize_t(c_stride) <= abs_py_ssize_t(f_stride):
        return 'C'
    else:
        return 'F'

1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
@cython.cdivision(True)
cdef void _copy_strided_to_strided(char *src_data, Py_ssize_t *src_strides,
                                   char *dst_data, Py_ssize_t *dst_strides,
                                   Py_ssize_t *src_shape, Py_ssize_t *dst_shape,
                                   int ndim, size_t itemsize) nogil:
    # Note: src_extent is 1 if we're broadcasting
    # dst_extent always >= src_extent as we don't do reductions
    cdef Py_ssize_t i
    cdef Py_ssize_t src_extent = src_shape[0]
    cdef Py_ssize_t dst_extent = dst_shape[0]
    cdef Py_ssize_t src_stride = src_strides[0]
    cdef Py_ssize_t dst_stride = dst_strides[0]
1146 1147

    if ndim == 1:
1148 1149 1150 1151 1152 1153 1154 1155
       if (src_stride > 0 and dst_stride > 0 and
           <size_t> src_stride == itemsize == <size_t> dst_stride):
           memcpy(dst_data, src_data, itemsize * dst_extent)
       else:
           for i in range(dst_extent):
               memcpy(dst_data, src_data, itemsize)
               src_data += src_stride
               dst_data += dst_stride
1156
    else:
1157 1158 1159 1160 1161 1162 1163
        for i in range(dst_extent):
            _copy_strided_to_strided(src_data, src_strides + 1,
                                     dst_data, dst_strides + 1,
                                     src_shape + 1, dst_shape + 1,
                                     ndim - 1, itemsize)
            src_data += src_stride
            dst_data += dst_stride
1164 1165 1166 1167 1168

cdef void copy_strided_to_strided({{memviewslice_name}} *src,
                                  {{memviewslice_name}} *dst,
                                  int ndim, size_t itemsize) nogil:
    _copy_strided_to_strided(src.data, src.strides, dst.data, dst.strides,
1169
                             src.shape, dst.shape, ndim, itemsize)
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181

@cname('__pyx_memoryview_slice_get_size')
cdef Py_ssize_t slice_get_size({{memviewslice_name}} *src, int ndim) nogil:
    "Return the size of the memory occupied by the slice in number of bytes"
    cdef int i
    cdef Py_ssize_t size = src.memview.view.itemsize

    for i in range(ndim):
        size *= src.shape[i]

    return size

1182 1183 1184 1185
@cname('__pyx_fill_contig_strides_array')
cdef Py_ssize_t fill_contig_strides_array(
                Py_ssize_t *shape, Py_ssize_t *strides, Py_ssize_t stride,
                int ndim, char order) nogil:
1186 1187 1188 1189
    """
    Fill the strides array for a slice with C or F contiguous strides.
    This is like PyBuffer_FillContiguousStrides, but compatible with py < 2.6
    """
1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
    cdef int idx

    if order == 'F':
        for idx in range(ndim):
            strides[idx] = stride
            stride = stride * shape[idx]
    else:
        for idx in range(ndim - 1, -1, -1):
            strides[idx] = stride
            stride = stride * shape[idx]

    return stride

1203
@cname('__pyx_memoryview_copy_data_to_temp')
1204 1205 1206 1207
cdef void *copy_data_to_temp({{memviewslice_name}} *src,
                             {{memviewslice_name}} *tmpslice,
                             char order,
                             int ndim) nogil except NULL:
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219
    """
    Copy a direct slice to temporary contiguous memory. The caller should free
    the result when done.
    """
    cdef int i
    cdef void *result

    cdef size_t itemsize = src.memview.view.itemsize
    cdef size_t size = slice_get_size(src, ndim)

    result = malloc(size)
    if not result:
1220
        _err(MemoryError, NULL)
1221

1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236
    # tmpslice[0] = src
    tmpslice.data = <char *> result
    tmpslice.memview = src.memview
    for i in range(ndim):
        tmpslice.shape[i] = src.shape[i]
        tmpslice.suboffsets[i] = -1

    fill_contig_strides_array(&tmpslice.shape[0], &tmpslice.strides[0], itemsize,
                              ndim, order)

    # We need to broadcast strides again
    for i in range(ndim):
        if tmpslice.shape[i] == 1:
            tmpslice.strides[i] = 0

1237
    if slice_is_contig(src[0], order, ndim):
1238 1239
        memcpy(result, src.data, size)
    else:
1240 1241
        copy_strided_to_strided(src, tmpslice, ndim, itemsize)

1242 1243
    return result

1244 1245 1246 1247 1248 1249 1250 1251 1252 1253
# Use 'with gil' functions and avoid 'with gil' blocks, as the code within the blocks
# has temporaries that need the GIL to clean up
@cname('__pyx_memoryview_err_extents')
cdef int _err_extents(int i, Py_ssize_t extent1,
                             Py_ssize_t extent2) except -1 with gil:
    raise ValueError("got differing extents in dimension %d (got %d and %d)" %
                                                        (i, extent1, extent2))

@cname('__pyx_memoryview_err_dim')
cdef int _err_dim(object error, char *msg, int dim) except -1 with gil:
1254
    raise error(msg.decode('ascii') % dim)
1255 1256 1257 1258

@cname('__pyx_memoryview_err')
cdef int _err(object error, char *msg) except -1 with gil:
    if msg != NULL:
1259
        raise error(msg.decode('ascii'))
1260 1261 1262
    else:
        raise error

1263
@cname('__pyx_memoryview_copy_contents')
1264 1265
cdef int memoryview_copy_contents({{memviewslice_name}} src,
                                  {{memviewslice_name}} dst,
1266 1267
                                  int src_ndim, int dst_ndim,
                                  bint dtype_is_object) nogil except -1:
1268 1269 1270 1271
    """
    Copy memory from slice src to slice dst.
    Check for overlapping memory and verify the shapes.
    """
1272
    cdef void *tmpdata = NULL
1273
    cdef size_t itemsize = src.memview.view.itemsize
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285
    cdef int i
    cdef char order = get_best_order(&src, src_ndim)
    cdef bint broadcasting = False
    cdef bint direct_copy = False
    cdef {{memviewslice_name}} tmp

    if src_ndim < dst_ndim:
        broadcast_leading(&src, src_ndim, dst_ndim)
    elif dst_ndim < src_ndim:
        broadcast_leading(&dst, dst_ndim, src_ndim)

    cdef int ndim = max(src_ndim, dst_ndim)
1286 1287 1288

    for i in range(ndim):
        if src.shape[i] != dst.shape[i]:
1289 1290 1291 1292
            if src.shape[i] == 1:
                broadcasting = True
                src.strides[i] = 0
            else:
1293
                _err_extents(i, dst.shape[i], src.shape[i])
1294 1295

        if src.suboffsets[i] >= 0:
1296
            _err_dim(ValueError, "Dimension %d is not direct", i)
1297

1298
    if slices_overlap(&src, &dst, ndim, itemsize):
1299
        # slices overlap, copy to temp, copy temp to dst
1300
        if not slice_is_contig(src, order, ndim):
1301 1302 1303 1304 1305 1306 1307 1308
            order = get_best_order(&dst, ndim)

        tmpdata = copy_data_to_temp(&src, &tmp, order, ndim)
        src = tmp

    if not broadcasting:
        # See if both slices have equal contiguity, in that case perform a
        # direct copy. This only works when we are not broadcasting.
1309 1310 1311 1312
        if slice_is_contig(src, 'C', ndim):
            direct_copy = slice_is_contig(dst, 'C', ndim)
        elif slice_is_contig(src, 'F', ndim):
            direct_copy = slice_is_contig(dst, 'F', ndim)
1313 1314 1315

        if direct_copy:
            # Contiguous slices with same order
1316
            refcount_copying(&dst, dtype_is_object, ndim, False)
1317
            memcpy(dst.data, src.data, slice_get_size(&src, ndim))
1318
            refcount_copying(&dst, dtype_is_object, ndim, True)
1319
            free(tmpdata)
1320 1321 1322
            return 0

    if order == 'F' == get_best_order(&dst, ndim):
1323 1324
        # see if both slices have Fortran order, transpose them to match our
        # C-style indexing order
1325 1326
        transpose_memslice(&src)
        transpose_memslice(&dst)
1327

1328
    refcount_copying(&dst, dtype_is_object, ndim, False)
1329
    copy_strided_to_strided(&src, &dst, ndim, itemsize)
1330 1331
    refcount_copying(&dst, dtype_is_object, ndim, True)

1332
    free(tmpdata)
1333 1334
    return 0

1335
@cname('__pyx_memoryview_broadcast_leading')
1336
cdef void broadcast_leading({{memviewslice_name}} *mslice,
1337 1338
                            int ndim,
                            int ndim_other) nogil:
1339
    cdef int i
1340
    cdef int offset = ndim_other - ndim
1341 1342

    for i in range(ndim - 1, -1, -1):
1343 1344 1345
        mslice.shape[i + offset] = mslice.shape[i]
        mslice.strides[i + offset] = mslice.strides[i]
        mslice.suboffsets[i + offset] = mslice.suboffsets[i]
1346 1347

    for i in range(offset):
1348 1349 1350
        mslice.shape[i] = 1
        mslice.strides[i] = mslice.strides[0]
        mslice.suboffsets[i] = -1
1351

1352
#
Unknown's avatar
Unknown committed
1353
### Take care of refcounting the objects in slices. Do this separately from any copying,
1354 1355 1356
### to minimize acquiring the GIL
#

1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388
@cname('__pyx_memoryview_refcount_copying')
cdef void refcount_copying({{memviewslice_name}} *dst, bint dtype_is_object,
                           int ndim, bint inc) nogil:
    # incref or decref the objects in the destination slice if the dtype is
    # object
    if dtype_is_object:
        refcount_objects_in_slice_with_gil(dst.data, dst.shape,
                                           dst.strides, ndim, inc)

@cname('__pyx_memoryview_refcount_objects_in_slice_with_gil')
cdef void refcount_objects_in_slice_with_gil(char *data, Py_ssize_t *shape,
                                             Py_ssize_t *strides, int ndim,
                                             bint inc) with gil:
    refcount_objects_in_slice(data, shape, strides, ndim, inc)

@cname('__pyx_memoryview_refcount_objects_in_slice')
cdef void refcount_objects_in_slice(char *data, Py_ssize_t *shape,
                                    Py_ssize_t *strides, int ndim, bint inc):
    cdef Py_ssize_t i

    for i in range(shape[0]):
        if ndim == 1:
            if inc:
                Py_INCREF((<PyObject **> data)[0])
            else:
                Py_DECREF((<PyObject **> data)[0])
        else:
            refcount_objects_in_slice(data, shape + 1, strides + 1,
                                      ndim - 1, inc)

        data += strides[0]

1389 1390 1391 1392 1393 1394 1395 1396
#
### Scalar to slice assignment
#
@cname('__pyx_memoryview_slice_assign_scalar')
cdef void slice_assign_scalar({{memviewslice_name}} *dst, int ndim,
                              size_t itemsize, void *item,
                              bint dtype_is_object) nogil:
    refcount_copying(dst, dtype_is_object, ndim, False)
1397 1398
    _slice_assign_scalar(dst.data, dst.shape, dst.strides, ndim,
                         itemsize, item)
1399 1400
    refcount_copying(dst, dtype_is_object, ndim, True)

1401

1402 1403 1404 1405 1406
@cname('__pyx_memoryview__slice_assign_scalar')
cdef void _slice_assign_scalar(char *data, Py_ssize_t *shape,
                              Py_ssize_t *strides, int ndim,
                              size_t itemsize, void *item) nogil:
    cdef Py_ssize_t i
1407 1408
    cdef Py_ssize_t stride = strides[0]
    cdef Py_ssize_t extent = shape[0]
1409

1410 1411
    if ndim == 1:
        for i in range(extent):
1412
            memcpy(data, item, itemsize)
1413 1414 1415
            data += stride
    else:
        for i in range(extent):
1416 1417
            _slice_assign_scalar(data, shape + 1, strides + 1,
                                ndim - 1, itemsize, item)
1418
            data += stride
1419

1420

1421 1422 1423
############### BufferFormatFromTypeInfo ###############
cdef extern from *:
    ctypedef struct __Pyx_StructField
1424

1425 1426 1427 1428
    cdef enum:
        __PYX_BUF_FLAGS_PACKED_STRUCT
        __PYX_BUF_FLAGS_INTEGER_COMPLEX

1429 1430 1431 1432
    ctypedef struct __Pyx_TypeInfo:
      char* name
      __Pyx_StructField* fields
      size_t size
1433 1434
      size_t arraysize[8]
      int ndim
1435 1436
      char typegroup
      char is_unsigned
1437
      int flags
1438

1439 1440 1441 1442
    ctypedef struct __Pyx_StructField:
      __Pyx_TypeInfo* type
      char* name
      size_t offset
1443

1444 1445 1446
    ctypedef struct __Pyx_BufFmt_StackElem:
      __Pyx_StructField* field
      size_t parent_offset
1447

1448 1449 1450
    #ctypedef struct __Pyx_BufFmt_Context:
    #  __Pyx_StructField root
      __Pyx_BufFmt_StackElem* head
1451

1452 1453
    struct __pyx_typeinfo_string:
        char string[3]
1454

1455
    __pyx_typeinfo_string __Pyx_TypeInfoToFormat(__Pyx_TypeInfo *)
1456 1457


1458
@cname('__pyx_format_from_typeinfo')
1459
cdef bytes format_from_typeinfo(__Pyx_TypeInfo *type):
1460 1461
    cdef __Pyx_StructField *field
    cdef __pyx_typeinfo_string fmt
1462
    cdef bytes part, result
1463

1464 1465
    if type.typegroup == 'S':
        assert type.fields != NULL and type.fields.type != NULL
1466

1467
        if type.flags & __PYX_BUF_FLAGS_PACKED_STRUCT:
1468
            alignment = b'^'
1469
        else:
1470
            alignment = b''
1471

1472
        parts = [b"T{"]
1473
        field = type.fields
1474

1475
        while field.type:
1476 1477
            part = format_from_typeinfo(field.type)
            parts.append(part + b':' + field.name + b':')
1478 1479
            field += 1

1480
        result = alignment.join(parts) + b'}'
1481 1482
    else:
        fmt = __Pyx_TypeInfoToFormat(type)
1483
        if type.arraysize[0]:
1484 1485
            extents = [unicode(type.arraysize[i]) for i in range(type.ndim)]
            result = (u"(%s)" % u','.join(extents)).encode('ascii') + fmt.string
1486
        else:
1487
            result = fmt.string
1488

1489
    return result