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