MemoryView.pyx 48.3 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
        PyBUF_STRIDES
        PyBUF_INDIRECT
67
        PyBUF_ND
68
        PyBUF_RECORDS
69
        PyBUF_RECORDS_RO
70

71 72 73
    ctypedef struct __Pyx_TypeInfo:
        pass

74 75 76
    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)
77

78 79 80 81 82 83 84 85
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" (
86
                            {{memviewslice_name}} mvs, char order, int ndim) nogil
87 88 89
    bint slices_overlap "__pyx_slices_overlap" ({{memviewslice_name}} *slice1,
                                                {{memviewslice_name}} *slice2,
                                                int ndim, size_t itemsize) nogil
90

91

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

97

98 99


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

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

    cdef:
        char *data
        Py_ssize_t len
        char *format
        int ndim
112 113
        Py_ssize_t *_shape
        Py_ssize_t *_strides
114
        Py_ssize_t itemsize
Stefan Behnel's avatar
Stefan Behnel committed
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 128
        cdef PyObject **p

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

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

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

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

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 148
        if not self._shape:
            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 157
        cdef char order
        if mode == 'fortran':
158
            order = b'F'
Stefan Behnel's avatar
Stefan Behnel committed
159
            self.mode = u'fortran'
160 161
        elif mode == 'c':
            order = b'C'
162
            self.mode = u'c'
163 164
        else:
            raise ValueError("Invalid mode, expected 'c' or 'fortran', got %s" % mode)
165 166 167

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

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

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

184
    @cname('getbuffer')
185 186
    def __getbuffer__(self, Py_buffer *info, int flags):
        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 191 192 193 194 195
            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
196 197
        info.shape = self._shape
        info.strides = self._strides
198 199
        info.suboffsets = NULL
        info.itemsize = self.itemsize
200
        info.readonly = 0
201 202 203 204 205 206

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

207 208
        info.obj = self

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

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

221 222 223 224 225 226 227 228
    @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)
229

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

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

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

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


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

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

    return result
256

257

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

262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
# 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.

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

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

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

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

@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

311 312 313 314 315

# 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
316 317
cdef int __pyx_memoryview_thread_locks_used = 0
cdef PyThread_type_lock[THREAD_LOCKS_PREALLOCATED] __pyx_memoryview_thread_locks = [
318 319 320 321 322 323 324 325 326 327 328
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
    PyThread_allocate_lock(),
]


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

    cdef object obj
333 334
    cdef object _size
    cdef object _array_interface
335
    cdef PyThread_type_lock lock
336 337 338 339
    # 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
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 349
        if type(self) is memoryview or obj is not None:
            __Pyx_GetBuffer(obj, &self.view, flags)
Mark Florisson's avatar
Mark Florisson committed
350 351
            if <PyObject *> self.view.obj == NULL:
                (<__pyx_buffer *> &self.view).obj = Py_None
352
                Py_INCREF(Py_None)
353

354 355 356 357
        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
358 359 360 361
        if self.lock is NULL:
            self.lock = PyThread_allocate_lock()
            if self.lock is NULL:
                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 366 367
        else:
            self.dtype_is_object = dtype_is_object

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

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

376
        cdef int i
377
        global __pyx_memoryview_thread_locks_used
378
        if self.lock != NULL:
379 380 381 382 383 384
            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])
385 386 387
                    break
            else:
                PyThread_free_lock(self.lock)
388

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

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

        return itemp
397

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

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

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

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

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

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

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

        return obj

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

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

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

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

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

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

            # 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:
475
            PyMem_Free(tmp)
476 477

    cdef setitem_indexed(self, index, value):
478 479 480 481 482 483 484 485
        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
486 487
        # Do a manual and complete check here instead of this easy hack
        bytesitem = itemp[:self.view.itemsize]
488 489 490 491 492 493 494 495
        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
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511

    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
512

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

518
        if flags & PyBUF_ND:
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
            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
542
        info.readonly = self.view.readonly
543 544
        info.obj = self

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

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

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

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

562 563 564 565 566
    @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")
567

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

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

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

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

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

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

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

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

597
            self._size = result
598

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

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

        return 0

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

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

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

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

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

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

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

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

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

649 650
        return memoryview_copy_from_slice(self, &dst)

651 652

@cname('__pyx_memoryview_new')
653 654 655 656 657 658
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')
659
cdef inline bint memoryview_check(object o):
660
    return isinstance(o, memoryview)
661

662
cdef tuple _unellipsify(object index, int ndim):
663 664 665 666
    """
    Replace all ellipses with full slices and fill incomplete indices with
    full slices.
    """
667 668 669 670 671 672 673 674 675 676 677
    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:
678
                result.extend([slice(None)] * (ndim - len(tup) + 1))
679
                seen_ellipsis = True
680 681 682 683
            else:
                result.append(slice(None))
            have_slices = True
        else:
684 685 686
            if not isinstance(item, slice) and not PyIndex_Check(item):
                raise TypeError("Cannot index with type '%s'" % type(item))

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

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

694
    return have_slices or nslices, tuple(result)
695

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

701 702 703
#
### Slicing a memoryview
#
704

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

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

716
    cdef _memoryviewslice memviewsliceobj
717

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

    if isinstance(memview, _memoryviewslice):
        memviewsliceobj = memview
        p_src = &memviewsliceobj.from_slice
    else:
724
        slice_copy(memview, &src)
725 726 727 728 729 730 731 732 733
        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

734 735 736 737 738 739 740 741
    # 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

742
    for dim, index in enumerate(indices):
743
        if PyIndex_Check(index):
744 745 746 747 748 749
            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)
750 751 752 753 754
        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
755
        else:
756 757 758 759 760 761 762 763
            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

764 765 766 767 768 769
            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)
770 771
            new_ndim += 1

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

781

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

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

790
cdef extern from "<stdio.h>":
791 792 793 794 795 796 797 798 799 800 801 802
    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')
803 804 805 806 807 808 809
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:
810 811 812 813 814 815 816 817 818
    """
    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
    """
819

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

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

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

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

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

866 867
        if not have_step:
            step = 1
868

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

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

876 877
        if new_shape < 0:
            new_shape = 0
878

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

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

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

900
    return 0
901 902 903 904

#
### Index a memoryview
#
905
@cname('__pyx_pybuffer_index')
906
cdef char *pybuffer_index(Py_buffer *view, char *bufp, Py_ssize_t index,
907
                          Py_ssize_t dim) except NULL:
908 909 910 911 912
    cdef Py_ssize_t shape, stride, suboffset = -1
    cdef Py_ssize_t itemsize = view.itemsize
    cdef char *resultp

    if view.ndim == 0:
913
        shape = view.len // itemsize
914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
        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

935 936 937 938
#
### Transposing a memoryviewslice
#
@cname('__pyx_memslice_transpose')
Mark Florisson's avatar
Mark Florisson committed
939
cdef int transpose_memslice({{memviewslice_name}} *memslice) nogil except 0:
940 941 942 943 944 945 946
    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
947
    for i in range(ndim // 2):
948 949 950 951 952
        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:
953
            _err(ValueError, "Cannot transpose memoryview with indirect dimensions")
954 955 956

    return 1

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

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

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

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

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

    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)

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

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


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

1001 1002
    cdef _memoryviewslice result

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

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

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

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

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

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

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

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

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

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

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

    return result

1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
@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

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

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

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

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

1075
@cname('__pyx_memoryview_copy_object')
1076
cdef memoryview_copy(memoryview memview):
1077
    "Create a new memoryview object"
1078
    cdef {{memviewslice_name}} memviewslice
1079 1080 1081 1082 1083 1084 1085 1086
    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.
    """
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
    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

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

1101

1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
#
### 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

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

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

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

1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146
@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]
1147 1148

    if ndim == 1:
1149 1150 1151 1152 1153 1154 1155 1156
       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
1157
    else:
1158 1159 1160 1161 1162 1163 1164
        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
1165 1166 1167 1168 1169

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,
1170
                             src.shape, dst.shape, ndim, itemsize)
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182

@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

1183 1184 1185 1186
@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:
1187 1188 1189 1190
    """
    Fill the strides array for a slice with C or F contiguous strides.
    This is like PyBuffer_FillContiguousStrides, but compatible with py < 2.6
    """
1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
    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

1204
@cname('__pyx_memoryview_copy_data_to_temp')
1205 1206 1207 1208
cdef void *copy_data_to_temp({{memviewslice_name}} *src,
                             {{memviewslice_name}} *tmpslice,
                             char order,
                             int ndim) nogil except NULL:
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
    """
    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:
1221
        _err(MemoryError, NULL)
1222

1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237
    # 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

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

1243 1244
    return result

1245 1246 1247 1248 1249 1250 1251 1252 1253 1254
# 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:
1255
    raise error(msg.decode('ascii') % dim)
1256 1257 1258 1259

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

1264
@cname('__pyx_memoryview_copy_contents')
1265 1266
cdef int memoryview_copy_contents({{memviewslice_name}} src,
                                  {{memviewslice_name}} dst,
1267 1268
                                  int src_ndim, int dst_ndim,
                                  bint dtype_is_object) nogil except -1:
1269 1270 1271 1272
    """
    Copy memory from slice src to slice dst.
    Check for overlapping memory and verify the shapes.
    """
1273
    cdef void *tmpdata = NULL
1274
    cdef size_t itemsize = src.memview.view.itemsize
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286
    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)
1287 1288 1289

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

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

1299
    if slices_overlap(&src, &dst, ndim, itemsize):
1300
        # slices overlap, copy to temp, copy temp to dst
1301
        if not slice_is_contig(src, order, ndim):
1302 1303 1304 1305 1306 1307 1308 1309
            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.
1310 1311 1312 1313
        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)
1314 1315 1316

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

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

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

1333
    free(tmpdata)
1334 1335
    return 0

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

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

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

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

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 1389
@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]

1390 1391 1392 1393 1394 1395 1396 1397
#
### 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)
1398 1399
    _slice_assign_scalar(dst.data, dst.shape, dst.strides, ndim,
                         itemsize, item)
1400 1401
    refcount_copying(dst, dtype_is_object, ndim, True)

1402

1403 1404 1405 1406 1407
@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
1408 1409
    cdef Py_ssize_t stride = strides[0]
    cdef Py_ssize_t extent = shape[0]
1410

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

1421

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

1426 1427 1428 1429
    cdef enum:
        __PYX_BUF_FLAGS_PACKED_STRUCT
        __PYX_BUF_FLAGS_INTEGER_COMPLEX

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

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

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

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

1453 1454
    struct __pyx_typeinfo_string:
        char string[3]
1455

1456
    __pyx_typeinfo_string __Pyx_TypeInfoToFormat(__Pyx_TypeInfo *)
1457 1458


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

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

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

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

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

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

1490
    return result