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) nogil 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 memory.") 187 info.buf = self.data 188 info.len = self.len 189 info.ndim = self.ndim 190 info.shape = self._shape 191 info.strides = self._strides 192 info.suboffsets = NULL 193 info.itemsize = self.itemsize 194 info.readonly = 0 195 196 if flags & PyBUF_FORMAT: 197 info.format = self.format 198 else: 199 info.format = NULL 200 201 info.obj = self 202 203 __pyx_getbuffer = capsule(<void *> &__pyx_array_getbuffer, "getbuffer(obj, view, flags)") 204 205 def __dealloc__(array self): 206 if self.callback_free_data != NULL: 207 self.callback_free_data(self.data) 208 elif self.free_data: 209 if self.dtype_is_object: 210 refcount_objects_in_slice(self.data, self._shape, 211 self._strides, self.ndim, False) 212 free(self.data) 213 PyMem_Free(self._shape) 214 215 property memview: 216 @cname('get_memview') 217 def __get__(self): 218 # Make this a property as 'self.data' may be set after instantiation 219 flags = PyBUF_ANY_CONTIGUOUS|PyBUF_FORMAT|PyBUF_WRITABLE 220 return memoryview(self, flags, self.dtype_is_object) 221 222 223 def __getattr__(self, attr): 224 return getattr(self.memview, attr) 225 226 def __getitem__(self, item): 227 return self.memview[item] 228 229 def __setitem__(self, item, value): 230 self.memview[item] = value 231 232 233 @cname("__pyx_array_new") 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 dimension 257 # 258 # e.g. 259 # int[::indirect_contiguous, ::contiguous, :] 260 # 261 # is valid (list of pointers to 2d fortran-contiguous array), but 262 # 263 # int[::generic_contiguous, ::contiguous, :] 264 # 265 # would mean you'd have assert dimension 0 to be indirect (and pointer contiguous) at runtime. 266 # So it doesn't bring any performance benefit, and it's only confusing. 267 268 @cname('__pyx_MemviewEnum') 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=False): 318 self.obj = obj 319 self.flags = flags 320 if type(self) is memoryview or obj is not None: 321 __Pyx_GetBuffer(obj, &self.view, flags) 322 if <PyObject *> self.view.obj == NULL: 323 (<__pyx_buffer *> &self.view).obj = Py_None 324 Py_INCREF(Py_None) 325 326 self.lock = PyThread_allocate_lock() 327 if self.lock == NULL: 328 raise MemoryError 329 330 if flags & PyBUF_FORMAT: 331 self.dtype_is_object = self.view.format == b'O' 332 else: 333 self.dtype_is_object = dtype_is_object 334 335 self.acquisition_count_aligned_p = <__pyx_atomic_int *> align_pointer( 336 <void *> &self.acquisition_count[0], sizeof(__pyx_atomic_int)) 337 self.typeinfo = NULL 338 339 def __dealloc__(memoryview self): 340 if self.obj is not None: 341 __Pyx_ReleaseBuffer(&self.view) 342 343 if self.lock != NULL: 344 PyThread_free_lock(self.lock) 345 346 cdef char *get_item_pointer(memoryview self, object index) except NULL: 347 cdef Py_ssize_t dim 348 cdef char *itemp = <char *> self.view.buf 349 350 for dim, idx in enumerate(index): 351 itemp = pybuffer_index(&self.view, itemp, idx, dim) 352 353 return itemp 354 355 #@cname('__pyx_memoryview_getitem') 356 def __getitem__(memoryview self, object index): 357 if index is Ellipsis: 358 return self 359 360 have_slices, indices = _unellipsify(index, self.view.ndim) 361 362 cdef char *itemp 363 if have_slices: 364 return memview_slice(self, indices) 365 else: 366 itemp = self.get_item_pointer(indices) 367 return self.convert_item_to_object(itemp) 368 369 def __setitem__(memoryview self, object index, object value): 370 have_slices, index = _unellipsify(index, self.view.ndim) 371 372 if have_slices: 373 obj = self.is_slice(value) 374 if obj: 375 self.setitem_slice_assignment(self[index], obj) 376 else: 377 self.setitem_slice_assign_scalar(self[index], value) 378 else: 379 self.setitem_indexed(index, value) 380 381 cdef is_slice(self, obj): 382 if not isinstance(obj, memoryview): 383 try: 384 obj = memoryview(obj, self.flags|PyBUF_ANY_CONTIGUOUS, 385 self.dtype_is_object) 386 except TypeError: 387 return None 388 389 return obj 390 391 cdef setitem_slice_assignment(self, dst, src): 392 cdef {{memviewslice_name}} dst_slice 393 cdef {{memviewslice_name}} src_slice 394 395 memoryview_copy_contents(get_slice_from_memview(src, &src_slice)[0], 396 get_slice_from_memview(dst, &dst_slice)[0], 397 src.ndim, dst.ndim, self.dtype_is_object) 398 399 cdef setitem_slice_assign_scalar(self, memoryview dst, value): 400 cdef int array[128] 401 cdef void *tmp = NULL 402 cdef void *item 403 404 cdef {{memviewslice_name}} *dst_slice 405 cdef {{memviewslice_name}} tmp_slice 406 dst_slice = get_slice_from_memview(dst, &tmp_slice) 407 408 if <size_t>self.view.itemsize > sizeof(array): 409 tmp = PyMem_Malloc(self.view.itemsize) 410 if tmp == NULL: 411 raise MemoryError 412 item = tmp 413 else: 414 item = <void *> array 415 416 try: 417 if self.dtype_is_object: 418 (<PyObject **> item)[0] = <PyObject *> value 419 else: 420 self.assign_item_from_object(<char *> item, value) 421 422 # It would be easy to support indirect dimensions, but it's easier 423 # to disallow :) 424 if self.view.suboffsets != NULL: 425 assert_direct_dimensions(self.view.suboffsets, self.view.ndim) 426 slice_assign_scalar(dst_slice, dst.view.ndim, self.view.itemsize, 427 item, self.dtype_is_object) 428 finally: 429 PyMem_Free(tmp) 430 431 cdef setitem_indexed(self, index, value): 432 cdef char *itemp = self.get_item_pointer(index) 433 self.assign_item_from_object(itemp, value) 434 435 cdef convert_item_to_object(self, char *itemp): 436 """Only used if instantiated manually by the user, or if Cython doesn't 437 know how to convert the type""" 438 import struct 439 cdef bytes bytesitem 440 # Do a manual and complete check here instead of this easy hack 441 bytesitem = itemp[:self.view.itemsize] 442 try: 443 result = struct.unpack(self.view.format, bytesitem) 444 except struct.error: 445 raise ValueError("Unable to convert item to object") 446 else: 447 if len(self.view.format) == 1: 448 return result[0] 449 return result 450 451 cdef assign_item_from_object(self, char *itemp, object value): 452 """Only used if instantiated manually by the user, or if Cython doesn't 453 know how to convert the type""" 454 import struct 455 cdef char c 456 cdef bytes bytesvalue 457 cdef Py_ssize_t i 458 459 if isinstance(value, tuple): 460 bytesvalue = struct.pack(self.view.format, *value) 461 else: 462 bytesvalue = struct.pack(self.view.format, value) 463 464 for i, c in enumerate(bytesvalue): 465 itemp[i] = c 466 467 @cname('getbuffer') 468 def __getbuffer__(self, Py_buffer *info, int flags): 469 if flags & PyBUF_STRIDES: 470 info.shape = self.view.shape 471 else: 472 info.shape = NULL 473 474 if flags & PyBUF_STRIDES: 475 info.strides = self.view.strides 476 else: 477 info.strides = NULL 478 479 if flags & PyBUF_INDIRECT: 480 info.suboffsets = self.view.suboffsets 481 else: 482 info.suboffsets = NULL 483 484 if flags & PyBUF_FORMAT: 485 info.format = self.view.format 486 else: 487 info.format = NULL 488 489 info.buf = self.view.buf 490 info.ndim = self.view.ndim 491 info.itemsize = self.view.itemsize 492 info.len = self.view.len 493 info.readonly = 0 494 info.obj = self 495 496 __pyx_getbuffer = capsule(<void *> &__pyx_memoryview_getbuffer, "getbuffer(obj, view, flags)") 497 498 # Some properties that have the same sematics as in NumPy 499 property T: 500 @cname('__pyx_memoryview_transpose') 501 def __get__(self): 502 cdef _memoryviewslice result = memoryview_copy(self) 503 transpose_memslice(&result.from_slice) 504 return result 505 506 property base: 507 @cname('__pyx_memoryview__get__base') 508 def __get__(self): 509 return self.obj 510 511 property shape: 512 @cname('__pyx_memoryview_get_shape') 513 def __get__(self): 514 return tuple([self.view.shape[i] for i in xrange(self.view.ndim)]) 515 516 property strides: 517 @cname('__pyx_memoryview_get_strides') 518 def __get__(self): 519 if self.view.strides == NULL: 520 # Note: we always ask for strides, so if this is not set it's a bug 521 raise ValueError("Buffer view does not expose strides") 522 523 return tuple([self.view.strides[i] for i in xrange(self.view.ndim)]) 524 525 property suboffsets: 526 @cname('__pyx_memoryview_get_suboffsets') 527 def __get__(self): 528 if self.view.suboffsets == NULL: 529 return [-1] * self.view.ndim 530 531 return tuple([self.view.suboffsets[i] for i in xrange(self.view.ndim)]) 532 533 property ndim: 534 @cname('__pyx_memoryview_get_ndim') 535 def __get__(self): 536 return self.view.ndim 537 538 property itemsize: 539 @cname('__pyx_memoryview_get_itemsize') 540 def __get__(self): 541 return self.view.itemsize 542 543 property nbytes: 544 @cname('__pyx_memoryview_get_nbytes') 545 def __get__(self): 546 return self.size * self.view.itemsize 547 548 property size: 549 @cname('__pyx_memoryview_get_size') 550 def __get__(self): 551 if self._size is None: 552 result = 1 553 554 for length in self.shape: 555 result *= length 556 557 self._size = result 558 559 return self._size 560 561 def __len__(self): 562 if self.view.ndim >= 1: 563 return self.view.shape[0] 564 565 return 0 566 567 def __repr__(self): 568 return "<MemoryView of %r at 0x%x>" % (self.base.__class__.__name__, 569 id(self)) 570 571 def __str__(self): 572 return "<MemoryView of %r object>" % (self.base.__class__.__name__,) 573 574 # Support the same attributes as memoryview slices 575 def is_c_contig(self): 576 cdef {{memviewslice_name}} *mslice 577 cdef {{memviewslice_name}} tmp 578 mslice = get_slice_from_memview(self, &tmp) 579 return slice_is_contig(mslice, 'C', self.view.ndim) 580 581 def is_f_contig(self): 582 cdef {{memviewslice_name}} *mslice 583 cdef {{memviewslice_name}} tmp 584 mslice = get_slice_from_memview(self, &tmp) 585 return slice_is_contig(mslice, 'F', self.view.ndim) 586 587 def copy(self): 588 cdef {{memviewslice_name}} mslice 589 cdef int flags = self.flags & ~PyBUF_F_CONTIGUOUS 590 591 slice_copy(self, &mslice) 592 mslice = slice_copy_contig(&mslice, "c", self.view.ndim, 593 self.view.itemsize, 594 flags|PyBUF_C_CONTIGUOUS, 595 self.dtype_is_object) 596 597 return memoryview_copy_from_slice(self, &mslice) 598 599 def copy_fortran(self): 600 cdef {{memviewslice_name}} src, dst 601 cdef int flags = self.flags & ~PyBUF_C_CONTIGUOUS 602 603 slice_copy(self, &src) 604 dst = slice_copy_contig(&src, "fortran", self.view.ndim, 605 self.view.itemsize, 606 flags|PyBUF_F_CONTIGUOUS, 607 self.dtype_is_object) 608 609 return memoryview_copy_from_slice(self, &dst) 610 611 612 @cname('__pyx_memoryview_new') 613 cdef memoryview_cwrapper(object o, int flags, bint dtype_is_object, __Pyx_TypeInfo *typeinfo): 614 cdef memoryview result = memoryview(o, flags, dtype_is_object) 615 result.typeinfo = typeinfo 616 return result 617 618 @cname('__pyx_memoryview_check') 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[dim], 707 dim, new_ndim, p_suboffset_dim, 708 index, 0, 0, # start, stop, step 709 0, 0, 0, # have_{start,stop,step} 710 False) 711 elif index is None: 712 p_dst.shape[new_ndim] = 1 713 p_dst.strides[new_ndim] = 0 714 p_dst.suboffsets[new_ndim] = -1 715 new_ndim += 1 716 else: 717 start = index.start or 0 718 stop = index.stop or 0 719 step = index.step or 0 720 721 have_start = index.start is not None 722 have_stop = index.stop is not None 723 have_step = index.step is not None 724 725 slice_memviewslice( 726 p_dst, p_src.shape[dim], p_src.strides[dim], p_src.suboffsets[dim], 727 dim, new_ndim, p_suboffset_dim, 728 start, stop, step, 729 have_start, have_stop, have_step, 730 True) 731 new_ndim += 1 732 733 if isinstance(memview, _memoryviewslice): 734 return memoryview_fromslice(dst, new_ndim, 735 memviewsliceobj.to_object_func, 736 memviewsliceobj.to_dtype_func, 737 memview.dtype_is_object) 738 else: 739 return memoryview_fromslice(dst, new_ndim, NULL, NULL, 740 memview.dtype_is_object) 741 742 743 # 744 ### Slicing in a single dimension of a memoryviewslice 745 # 746 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 dimensions") 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(obj, 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}} *memviewslice): 1041 """ 1042 Create a new memoryview object from a given memoryview object and slice. 1043 """ 1044 cdef object (*to_object_func)(char *) 1045 cdef int (*to_dtype_func)(char *, object) except 0 1046 1047 if isinstance(memview, _memoryviewslice): 1048 to_object_func = (<_memoryviewslice> memview).to_object_func 1049 to_dtype_func = (<_memoryviewslice> memview).to_dtype_func 1050 else: 1051 to_object_func = NULL 1052 to_dtype_func = NULL 1053 1054 return memoryview_fromslice(memviewslice[0], memview.view.ndim, 1055 to_object_func, to_dtype_func, 1056 memview.dtype_is_object) 1057 1058 1059 # 1060 ### Copy the contents of a memoryview slices 1061 # 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 blocks 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 1448