1from __future__ import absolute_import 2 3from .Errors import CompileError, error 4from . import ExprNodes 5from .ExprNodes import IntNode, NameNode, AttributeNode 6from . import Options 7from .Code import UtilityCode, TempitaUtilityCode 8from .UtilityCode import CythonUtilityCode 9from . import Buffer 10from . import PyrexTypes 11from . import ModuleNode 12 13START_ERR = "Start must not be given." 14STOP_ERR = "Axis specification only allowed in the 'step' slot." 15STEP_ERR = "Step must be omitted, 1, or a valid specifier." 16BOTH_CF_ERR = "Cannot specify an array that is both C and Fortran contiguous." 17INVALID_ERR = "Invalid axis specification." 18NOT_CIMPORTED_ERR = "Variable was not cimported from cython.view" 19EXPR_ERR = "no expressions allowed in axis spec, only names and literals." 20CF_ERR = "Invalid axis specification for a C/Fortran contiguous array." 21ERR_UNINITIALIZED = ("Cannot check if memoryview %s is initialized without the " 22 "GIL, consider using initializedcheck(False)") 23 24 25def concat_flags(*flags): 26 return "(%s)" % "|".join(flags) 27 28 29format_flag = "PyBUF_FORMAT" 30 31memview_c_contiguous = "(PyBUF_C_CONTIGUOUS | PyBUF_FORMAT)" 32memview_f_contiguous = "(PyBUF_F_CONTIGUOUS | PyBUF_FORMAT)" 33memview_any_contiguous = "(PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT)" 34memview_full_access = "PyBUF_FULL_RO" 35#memview_strided_access = "PyBUF_STRIDED_RO" 36memview_strided_access = "PyBUF_RECORDS_RO" 37 38MEMVIEW_DIRECT = '__Pyx_MEMVIEW_DIRECT' 39MEMVIEW_PTR = '__Pyx_MEMVIEW_PTR' 40MEMVIEW_FULL = '__Pyx_MEMVIEW_FULL' 41MEMVIEW_CONTIG = '__Pyx_MEMVIEW_CONTIG' 42MEMVIEW_STRIDED= '__Pyx_MEMVIEW_STRIDED' 43MEMVIEW_FOLLOW = '__Pyx_MEMVIEW_FOLLOW' 44 45_spec_to_const = { 46 'direct' : MEMVIEW_DIRECT, 47 'ptr' : MEMVIEW_PTR, 48 'full' : MEMVIEW_FULL, 49 'contig' : MEMVIEW_CONTIG, 50 'strided': MEMVIEW_STRIDED, 51 'follow' : MEMVIEW_FOLLOW, 52 } 53 54_spec_to_abbrev = { 55 'direct' : 'd', 56 'ptr' : 'p', 57 'full' : 'f', 58 'contig' : 'c', 59 'strided' : 's', 60 'follow' : '_', 61} 62 63memslice_entry_init = "{ 0, 0, { 0 }, { 0 }, { 0 } }" 64 65memview_name = u'memoryview' 66memview_typeptr_cname = '__pyx_memoryview_type' 67memview_objstruct_cname = '__pyx_memoryview_obj' 68memviewslice_cname = u'__Pyx_memviewslice' 69 70 71def put_init_entry(mv_cname, code): 72 code.putln("%s.data = NULL;" % mv_cname) 73 code.putln("%s.memview = NULL;" % mv_cname) 74 75 76#def axes_to_str(axes): 77# return "".join([access[0].upper()+packing[0] for (access, packing) in axes]) 78 79 80def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code, 81 have_gil=False, first_assignment=True): 82 "We can avoid decreffing the lhs if we know it is the first assignment" 83 assert rhs.type.is_memoryviewslice 84 85 pretty_rhs = rhs.result_in_temp() or rhs.is_simple() 86 if pretty_rhs: 87 rhstmp = rhs.result() 88 else: 89 rhstmp = code.funcstate.allocate_temp(lhs_type, manage_ref=False) 90 code.putln("%s = %s;" % (rhstmp, rhs.result_as(lhs_type))) 91 92 # Allow uninitialized assignment 93 #code.putln(code.put_error_if_unbound(lhs_pos, rhs.entry)) 94 put_assign_to_memviewslice(lhs_cname, rhs, rhstmp, lhs_type, code, 95 have_gil=have_gil, first_assignment=first_assignment) 96 97 if not pretty_rhs: 98 code.funcstate.release_temp(rhstmp) 99 100 101def put_assign_to_memviewslice(lhs_cname, rhs, rhs_cname, memviewslicetype, code, 102 have_gil=False, first_assignment=False): 103 if not first_assignment: 104 code.put_xdecref_memoryviewslice(lhs_cname, have_gil=have_gil) 105 106 if not rhs.result_in_temp(): 107 rhs.make_owned_memoryviewslice(code) 108 109 code.putln("%s = %s;" % (lhs_cname, rhs_cname)) 110 111 112def get_buf_flags(specs): 113 is_c_contig, is_f_contig = is_cf_contig(specs) 114 115 if is_c_contig: 116 return memview_c_contiguous 117 elif is_f_contig: 118 return memview_f_contiguous 119 120 access, packing = zip(*specs) 121 122 if 'full' in access or 'ptr' in access: 123 return memview_full_access 124 else: 125 return memview_strided_access 126 127 128def insert_newaxes(memoryviewtype, n): 129 axes = [('direct', 'strided')] * n 130 axes.extend(memoryviewtype.axes) 131 return PyrexTypes.MemoryViewSliceType(memoryviewtype.dtype, axes) 132 133 134def broadcast_types(src, dst): 135 n = abs(src.ndim - dst.ndim) 136 if src.ndim < dst.ndim: 137 return insert_newaxes(src, n), dst 138 else: 139 return src, insert_newaxes(dst, n) 140 141 142def valid_memslice_dtype(dtype, i=0): 143 """ 144 Return whether type dtype can be used as the base type of a 145 memoryview slice. 146 147 We support structs, numeric types and objects 148 """ 149 if dtype.is_complex and dtype.real_type.is_int: 150 return False 151 152 if dtype is PyrexTypes.c_bint_type: 153 return False 154 155 if dtype.is_struct and dtype.kind == 'struct': 156 for member in dtype.scope.var_entries: 157 if not valid_memslice_dtype(member.type): 158 return False 159 160 return True 161 162 return ( 163 dtype.is_error or 164 # Pointers are not valid (yet) 165 # (dtype.is_ptr and valid_memslice_dtype(dtype.base_type)) or 166 (dtype.is_array and i < 8 and 167 valid_memslice_dtype(dtype.base_type, i + 1)) or 168 dtype.is_numeric or 169 dtype.is_pyobject or 170 dtype.is_fused or # accept this as it will be replaced by specializations later 171 (dtype.is_typedef and valid_memslice_dtype(dtype.typedef_base_type)) 172 ) 173 174 175class MemoryViewSliceBufferEntry(Buffer.BufferEntry): 176 """ 177 May be used during code generation time to be queried for 178 shape/strides/suboffsets attributes, or to perform indexing or slicing. 179 """ 180 def __init__(self, entry): 181 self.entry = entry 182 self.type = entry.type 183 self.cname = entry.cname 184 185 self.buf_ptr = "%s.data" % self.cname 186 187 dtype = self.entry.type.dtype 188 self.buf_ptr_type = PyrexTypes.CPtrType(dtype) 189 self.init_attributes() 190 191 def get_buf_suboffsetvars(self): 192 return self._for_all_ndim("%s.suboffsets[%d]") 193 194 def get_buf_stridevars(self): 195 return self._for_all_ndim("%s.strides[%d]") 196 197 def get_buf_shapevars(self): 198 return self._for_all_ndim("%s.shape[%d]") 199 200 def generate_buffer_lookup_code(self, code, index_cnames): 201 axes = [(dim, index_cnames[dim], access, packing) 202 for dim, (access, packing) in enumerate(self.type.axes)] 203 return self._generate_buffer_lookup_code(code, axes) 204 205 def _generate_buffer_lookup_code(self, code, axes, cast_result=True): 206 """ 207 Generate a single expression that indexes the memory view slice 208 in each dimension. 209 """ 210 bufp = self.buf_ptr 211 type_decl = self.type.dtype.empty_declaration_code() 212 213 for dim, index, access, packing in axes: 214 shape = "%s.shape[%d]" % (self.cname, dim) 215 stride = "%s.strides[%d]" % (self.cname, dim) 216 suboffset = "%s.suboffsets[%d]" % (self.cname, dim) 217 218 flag = get_memoryview_flag(access, packing) 219 220 if flag in ("generic", "generic_contiguous"): 221 # Note: we cannot do cast tricks to avoid stride multiplication 222 # for generic_contiguous, as we may have to do (dtype *) 223 # or (dtype **) arithmetic, we won't know which unless 224 # we check suboffsets 225 code.globalstate.use_utility_code(memviewslice_index_helpers) 226 bufp = ('__pyx_memviewslice_index_full(%s, %s, %s, %s)' % 227 (bufp, index, stride, suboffset)) 228 229 elif flag == "indirect": 230 bufp = "(%s + %s * %s)" % (bufp, index, stride) 231 bufp = ("(*((char **) %s) + %s)" % (bufp, suboffset)) 232 233 elif flag == "indirect_contiguous": 234 # Note: we do char ** arithmetic 235 bufp = "(*((char **) %s + %s) + %s)" % (bufp, index, suboffset) 236 237 elif flag == "strided": 238 bufp = "(%s + %s * %s)" % (bufp, index, stride) 239 240 else: 241 assert flag == 'contiguous', flag 242 bufp = '((char *) (((%s *) %s) + %s))' % (type_decl, bufp, index) 243 244 bufp = '( /* dim=%d */ %s )' % (dim, bufp) 245 246 if cast_result: 247 return "((%s *) %s)" % (type_decl, bufp) 248 249 return bufp 250 251 def generate_buffer_slice_code(self, code, indices, dst, have_gil, 252 have_slices, directives): 253 """ 254 Slice a memoryviewslice. 255 256 indices - list of index nodes. If not a SliceNode, or NoneNode, 257 then it must be coercible to Py_ssize_t 258 259 Simply call __pyx_memoryview_slice_memviewslice with the right 260 arguments, unless the dimension is omitted or a bare ':', in which 261 case we copy over the shape/strides/suboffsets attributes directly 262 for that dimension. 263 """ 264 src = self.cname 265 266 code.putln("%(dst)s.data = %(src)s.data;" % locals()) 267 code.putln("%(dst)s.memview = %(src)s.memview;" % locals()) 268 code.put_incref_memoryviewslice(dst) 269 270 all_dimensions_direct = all(access == 'direct' for access, packing in self.type.axes) 271 suboffset_dim_temp = [] 272 273 def get_suboffset_dim(): 274 # create global temp variable at request 275 if not suboffset_dim_temp: 276 suboffset_dim = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False) 277 code.putln("%s = -1;" % suboffset_dim) 278 suboffset_dim_temp.append(suboffset_dim) 279 return suboffset_dim_temp[0] 280 281 dim = -1 282 new_ndim = 0 283 for index in indices: 284 if index.is_none: 285 # newaxis 286 for attrib, value in [('shape', 1), ('strides', 0), ('suboffsets', -1)]: 287 code.putln("%s.%s[%d] = %d;" % (dst, attrib, new_ndim, value)) 288 289 new_ndim += 1 290 continue 291 292 dim += 1 293 access, packing = self.type.axes[dim] 294 295 if isinstance(index, ExprNodes.SliceNode): 296 # slice, unspecified dimension, or part of ellipsis 297 d = dict(locals()) 298 for s in "start stop step".split(): 299 idx = getattr(index, s) 300 have_idx = d['have_' + s] = not idx.is_none 301 d[s] = idx.result() if have_idx else "0" 302 303 if not (d['have_start'] or d['have_stop'] or d['have_step']): 304 # full slice (:), simply copy over the extent, stride 305 # and suboffset. Also update suboffset_dim if needed 306 d['access'] = access 307 util_name = "SimpleSlice" 308 else: 309 util_name = "ToughSlice" 310 d['error_goto'] = code.error_goto(index.pos) 311 312 new_ndim += 1 313 else: 314 # normal index 315 idx = index.result() 316 317 indirect = access != 'direct' 318 if indirect: 319 generic = access == 'full' 320 if new_ndim != 0: 321 return error(index.pos, 322 "All preceding dimensions must be " 323 "indexed and not sliced") 324 325 d = dict( 326 locals(), 327 wraparound=int(directives['wraparound']), 328 boundscheck=int(directives['boundscheck']), 329 ) 330 if d['boundscheck']: 331 d['error_goto'] = code.error_goto(index.pos) 332 util_name = "SliceIndex" 333 334 _, impl = TempitaUtilityCode.load_as_string(util_name, "MemoryView_C.c", context=d) 335 code.put(impl) 336 337 if suboffset_dim_temp: 338 code.funcstate.release_temp(suboffset_dim_temp[0]) 339 340 341def empty_slice(pos): 342 none = ExprNodes.NoneNode(pos) 343 return ExprNodes.SliceNode(pos, start=none, 344 stop=none, step=none) 345 346 347def unellipsify(indices, ndim): 348 result = [] 349 seen_ellipsis = False 350 have_slices = False 351 352 newaxes = [newaxis for newaxis in indices if newaxis.is_none] 353 n_indices = len(indices) - len(newaxes) 354 355 for index in indices: 356 if isinstance(index, ExprNodes.EllipsisNode): 357 have_slices = True 358 full_slice = empty_slice(index.pos) 359 360 if seen_ellipsis: 361 result.append(full_slice) 362 else: 363 nslices = ndim - n_indices + 1 364 result.extend([full_slice] * nslices) 365 seen_ellipsis = True 366 else: 367 have_slices = have_slices or index.is_slice or index.is_none 368 result.append(index) 369 370 result_length = len(result) - len(newaxes) 371 if result_length < ndim: 372 have_slices = True 373 nslices = ndim - result_length 374 result.extend([empty_slice(indices[-1].pos)] * nslices) 375 376 return have_slices, result, newaxes 377 378 379def get_memoryview_flag(access, packing): 380 if access == 'full' and packing in ('strided', 'follow'): 381 return 'generic' 382 elif access == 'full' and packing == 'contig': 383 return 'generic_contiguous' 384 elif access == 'ptr' and packing in ('strided', 'follow'): 385 return 'indirect' 386 elif access == 'ptr' and packing == 'contig': 387 return 'indirect_contiguous' 388 elif access == 'direct' and packing in ('strided', 'follow'): 389 return 'strided' 390 else: 391 assert (access, packing) == ('direct', 'contig'), (access, packing) 392 return 'contiguous' 393 394 395def get_is_contig_func_name(contig_type, ndim): 396 assert contig_type in ('C', 'F') 397 return "__pyx_memviewslice_is_contig_%s%d" % (contig_type, ndim) 398 399 400def get_is_contig_utility(contig_type, ndim): 401 assert contig_type in ('C', 'F') 402 C = dict(context, ndim=ndim, contig_type=contig_type) 403 utility = load_memview_c_utility("MemviewSliceCheckContig", C, requires=[is_contig_utility]) 404 return utility 405 406 407def slice_iter(slice_type, slice_result, ndim, code): 408 if slice_type.is_c_contig or slice_type.is_f_contig: 409 return ContigSliceIter(slice_type, slice_result, ndim, code) 410 else: 411 return StridedSliceIter(slice_type, slice_result, ndim, code) 412 413 414class SliceIter(object): 415 def __init__(self, slice_type, slice_result, ndim, code): 416 self.slice_type = slice_type 417 self.slice_result = slice_result 418 self.code = code 419 self.ndim = ndim 420 421 422class ContigSliceIter(SliceIter): 423 def start_loops(self): 424 code = self.code 425 code.begin_block() 426 427 type_decl = self.slice_type.dtype.empty_declaration_code() 428 429 total_size = ' * '.join("%s.shape[%d]" % (self.slice_result, i) 430 for i in range(self.ndim)) 431 code.putln("Py_ssize_t __pyx_temp_extent = %s;" % total_size) 432 code.putln("Py_ssize_t __pyx_temp_idx;") 433 code.putln("%s *__pyx_temp_pointer = (%s *) %s.data;" % ( 434 type_decl, type_decl, self.slice_result)) 435 code.putln("for (__pyx_temp_idx = 0; " 436 "__pyx_temp_idx < __pyx_temp_extent; " 437 "__pyx_temp_idx++) {") 438 439 return "__pyx_temp_pointer" 440 441 def end_loops(self): 442 self.code.putln("__pyx_temp_pointer += 1;") 443 self.code.putln("}") 444 self.code.end_block() 445 446 447class StridedSliceIter(SliceIter): 448 def start_loops(self): 449 code = self.code 450 code.begin_block() 451 452 for i in range(self.ndim): 453 t = i, self.slice_result, i 454 code.putln("Py_ssize_t __pyx_temp_extent_%d = %s.shape[%d];" % t) 455 code.putln("Py_ssize_t __pyx_temp_stride_%d = %s.strides[%d];" % t) 456 code.putln("char *__pyx_temp_pointer_%d;" % i) 457 code.putln("Py_ssize_t __pyx_temp_idx_%d;" % i) 458 459 code.putln("__pyx_temp_pointer_0 = %s.data;" % self.slice_result) 460 461 for i in range(self.ndim): 462 if i > 0: 463 code.putln("__pyx_temp_pointer_%d = __pyx_temp_pointer_%d;" % (i, i - 1)) 464 465 code.putln("for (__pyx_temp_idx_%d = 0; " 466 "__pyx_temp_idx_%d < __pyx_temp_extent_%d; " 467 "__pyx_temp_idx_%d++) {" % (i, i, i, i)) 468 469 return "__pyx_temp_pointer_%d" % (self.ndim - 1) 470 471 def end_loops(self): 472 code = self.code 473 for i in range(self.ndim - 1, -1, -1): 474 code.putln("__pyx_temp_pointer_%d += __pyx_temp_stride_%d;" % (i, i)) 475 code.putln("}") 476 477 code.end_block() 478 479 480def copy_c_or_fortran_cname(memview): 481 if memview.is_c_contig: 482 c_or_f = 'c' 483 else: 484 c_or_f = 'f' 485 486 return "__pyx_memoryview_copy_slice_%s_%s" % ( 487 memview.specialization_suffix(), c_or_f) 488 489 490def get_copy_new_utility(pos, from_memview, to_memview): 491 if (from_memview.dtype != to_memview.dtype and 492 not (from_memview.dtype.is_const and from_memview.dtype.const_base_type == to_memview.dtype)): 493 error(pos, "dtypes must be the same!") 494 return 495 if len(from_memview.axes) != len(to_memview.axes): 496 error(pos, "number of dimensions must be same") 497 return 498 if not (to_memview.is_c_contig or to_memview.is_f_contig): 499 error(pos, "to_memview must be c or f contiguous.") 500 return 501 502 for (access, packing) in from_memview.axes: 503 if access != 'direct': 504 error(pos, "cannot handle 'full' or 'ptr' access at this time.") 505 return 506 507 if to_memview.is_c_contig: 508 mode = 'c' 509 contig_flag = memview_c_contiguous 510 elif to_memview.is_f_contig: 511 mode = 'fortran' 512 contig_flag = memview_f_contiguous 513 514 return load_memview_c_utility( 515 "CopyContentsUtility", 516 context=dict( 517 context, 518 mode=mode, 519 dtype_decl=to_memview.dtype.empty_declaration_code(), 520 contig_flag=contig_flag, 521 ndim=to_memview.ndim, 522 func_cname=copy_c_or_fortran_cname(to_memview), 523 dtype_is_object=int(to_memview.dtype.is_pyobject)), 524 requires=[copy_contents_new_utility]) 525 526 527def get_axes_specs(env, axes): 528 ''' 529 get_axes_specs(env, axes) -> list of (access, packing) specs for each axis. 530 access is one of 'full', 'ptr' or 'direct' 531 packing is one of 'contig', 'strided' or 'follow' 532 ''' 533 534 cythonscope = env.global_scope().context.cython_scope 535 cythonscope.load_cythonscope() 536 viewscope = cythonscope.viewscope 537 538 access_specs = tuple([viewscope.lookup(name) 539 for name in ('full', 'direct', 'ptr')]) 540 packing_specs = tuple([viewscope.lookup(name) 541 for name in ('contig', 'strided', 'follow')]) 542 543 is_f_contig, is_c_contig = False, False 544 default_access, default_packing = 'direct', 'strided' 545 cf_access, cf_packing = default_access, 'follow' 546 547 axes_specs = [] 548 # analyse all axes. 549 for idx, axis in enumerate(axes): 550 if not axis.start.is_none: 551 raise CompileError(axis.start.pos, START_ERR) 552 553 if not axis.stop.is_none: 554 raise CompileError(axis.stop.pos, STOP_ERR) 555 556 if axis.step.is_none: 557 axes_specs.append((default_access, default_packing)) 558 559 elif isinstance(axis.step, IntNode): 560 # the packing for the ::1 axis is contiguous, 561 # all others are cf_packing. 562 if axis.step.compile_time_value(env) != 1: 563 raise CompileError(axis.step.pos, STEP_ERR) 564 565 axes_specs.append((cf_access, 'cfcontig')) 566 567 elif isinstance(axis.step, (NameNode, AttributeNode)): 568 entry = _get_resolved_spec(env, axis.step) 569 if entry.name in view_constant_to_access_packing: 570 axes_specs.append(view_constant_to_access_packing[entry.name]) 571 else: 572 raise CompileError(axis.step.pos, INVALID_ERR) 573 574 else: 575 raise CompileError(axis.step.pos, INVALID_ERR) 576 577 # First, find out if we have a ::1 somewhere 578 contig_dim = 0 579 is_contig = False 580 for idx, (access, packing) in enumerate(axes_specs): 581 if packing == 'cfcontig': 582 if is_contig: 583 raise CompileError(axis.step.pos, BOTH_CF_ERR) 584 585 contig_dim = idx 586 axes_specs[idx] = (access, 'contig') 587 is_contig = True 588 589 if is_contig: 590 # We have a ::1 somewhere, see if we're C or Fortran contiguous 591 if contig_dim == len(axes) - 1: 592 is_c_contig = True 593 else: 594 is_f_contig = True 595 596 if contig_dim and not axes_specs[contig_dim - 1][0] in ('full', 'ptr'): 597 raise CompileError(axes[contig_dim].pos, 598 "Fortran contiguous specifier must follow an indirect dimension") 599 600 if is_c_contig: 601 # Contiguous in the last dimension, find the last indirect dimension 602 contig_dim = -1 603 for idx, (access, packing) in enumerate(reversed(axes_specs)): 604 if access in ('ptr', 'full'): 605 contig_dim = len(axes) - idx - 1 606 607 # Replace 'strided' with 'follow' for any dimension following the last 608 # indirect dimension, the first dimension or the dimension following 609 # the ::1. 610 # int[::indirect, ::1, :, :] 611 # ^ ^ 612 # int[::indirect, :, :, ::1] 613 # ^ ^ 614 start = contig_dim + 1 615 stop = len(axes) - is_c_contig 616 for idx, (access, packing) in enumerate(axes_specs[start:stop]): 617 idx = contig_dim + 1 + idx 618 if access != 'direct': 619 raise CompileError(axes[idx].pos, 620 "Indirect dimension may not follow " 621 "Fortran contiguous dimension") 622 if packing == 'contig': 623 raise CompileError(axes[idx].pos, 624 "Dimension may not be contiguous") 625 axes_specs[idx] = (access, cf_packing) 626 627 if is_c_contig: 628 # For C contiguity, we need to fix the 'contig' dimension 629 # after the loop 630 a, p = axes_specs[-1] 631 axes_specs[-1] = a, 'contig' 632 633 validate_axes_specs([axis.start.pos for axis in axes], 634 axes_specs, 635 is_c_contig, 636 is_f_contig) 637 638 return axes_specs 639 640 641def validate_axes(pos, axes): 642 if len(axes) >= Options.buffer_max_dims: 643 error(pos, "More dimensions than the maximum number" 644 " of buffer dimensions were used.") 645 return False 646 647 return True 648 649 650def is_cf_contig(specs): 651 is_c_contig = is_f_contig = False 652 653 if len(specs) == 1 and specs == [('direct', 'contig')]: 654 is_c_contig = True 655 656 elif (specs[-1] == ('direct','contig') and 657 all(axis == ('direct','follow') for axis in specs[:-1])): 658 # c_contiguous: 'follow', 'follow', ..., 'follow', 'contig' 659 is_c_contig = True 660 661 elif (len(specs) > 1 and 662 specs[0] == ('direct','contig') and 663 all(axis == ('direct','follow') for axis in specs[1:])): 664 # f_contiguous: 'contig', 'follow', 'follow', ..., 'follow' 665 is_f_contig = True 666 667 return is_c_contig, is_f_contig 668 669 670def get_mode(specs): 671 is_c_contig, is_f_contig = is_cf_contig(specs) 672 673 if is_c_contig: 674 return 'c' 675 elif is_f_contig: 676 return 'fortran' 677 678 for access, packing in specs: 679 if access in ('ptr', 'full'): 680 return 'full' 681 682 return 'strided' 683 684view_constant_to_access_packing = { 685 'generic': ('full', 'strided'), 686 'strided': ('direct', 'strided'), 687 'indirect': ('ptr', 'strided'), 688 'generic_contiguous': ('full', 'contig'), 689 'contiguous': ('direct', 'contig'), 690 'indirect_contiguous': ('ptr', 'contig'), 691} 692 693def validate_axes_specs(positions, specs, is_c_contig, is_f_contig): 694 695 packing_specs = ('contig', 'strided', 'follow') 696 access_specs = ('direct', 'ptr', 'full') 697 698 # is_c_contig, is_f_contig = is_cf_contig(specs) 699 700 has_contig = has_follow = has_strided = has_generic_contig = False 701 702 last_indirect_dimension = -1 703 for idx, (access, packing) in enumerate(specs): 704 if access == 'ptr': 705 last_indirect_dimension = idx 706 707 for idx, (pos, (access, packing)) in enumerate(zip(positions, specs)): 708 709 if not (access in access_specs and 710 packing in packing_specs): 711 raise CompileError(pos, "Invalid axes specification.") 712 713 if packing == 'strided': 714 has_strided = True 715 elif packing == 'contig': 716 if has_contig: 717 raise CompileError(pos, "Only one direct contiguous " 718 "axis may be specified.") 719 720 valid_contig_dims = last_indirect_dimension + 1, len(specs) - 1 721 if idx not in valid_contig_dims and access != 'ptr': 722 if last_indirect_dimension + 1 != len(specs) - 1: 723 dims = "dimensions %d and %d" % valid_contig_dims 724 else: 725 dims = "dimension %d" % valid_contig_dims[0] 726 727 raise CompileError(pos, "Only %s may be contiguous and direct" % dims) 728 729 has_contig = access != 'ptr' 730 elif packing == 'follow': 731 if has_strided: 732 raise CompileError(pos, "A memoryview cannot have both follow and strided axis specifiers.") 733 if not (is_c_contig or is_f_contig): 734 raise CompileError(pos, "Invalid use of the follow specifier.") 735 736 if access in ('ptr', 'full'): 737 has_strided = False 738 739def _get_resolved_spec(env, spec): 740 # spec must be a NameNode or an AttributeNode 741 if isinstance(spec, NameNode): 742 return _resolve_NameNode(env, spec) 743 elif isinstance(spec, AttributeNode): 744 return _resolve_AttributeNode(env, spec) 745 else: 746 raise CompileError(spec.pos, INVALID_ERR) 747 748def _resolve_NameNode(env, node): 749 try: 750 resolved_name = env.lookup(node.name).name 751 except AttributeError: 752 raise CompileError(node.pos, INVALID_ERR) 753 754 viewscope = env.global_scope().context.cython_scope.viewscope 755 entry = viewscope.lookup(resolved_name) 756 if entry is None: 757 raise CompileError(node.pos, NOT_CIMPORTED_ERR) 758 759 return entry 760 761def _resolve_AttributeNode(env, node): 762 path = [] 763 while isinstance(node, AttributeNode): 764 path.insert(0, node.attribute) 765 node = node.obj 766 if isinstance(node, NameNode): 767 path.insert(0, node.name) 768 else: 769 raise CompileError(node.pos, EXPR_ERR) 770 modnames = path[:-1] 771 # must be at least 1 module name, o/w not an AttributeNode. 772 assert modnames 773 774 scope = env 775 for modname in modnames: 776 mod = scope.lookup(modname) 777 if not mod or not mod.as_module: 778 raise CompileError( 779 node.pos, "undeclared name not builtin: %s" % modname) 780 scope = mod.as_module 781 782 entry = scope.lookup(path[-1]) 783 if not entry: 784 raise CompileError(node.pos, "No such attribute '%s'" % path[-1]) 785 786 return entry 787 788# 789### Utility loading 790# 791 792def load_memview_cy_utility(util_code_name, context=None, **kwargs): 793 return CythonUtilityCode.load(util_code_name, "MemoryView.pyx", 794 context=context, **kwargs) 795 796def load_memview_c_utility(util_code_name, context=None, **kwargs): 797 if context is None: 798 return UtilityCode.load(util_code_name, "MemoryView_C.c", **kwargs) 799 else: 800 return TempitaUtilityCode.load(util_code_name, "MemoryView_C.c", 801 context=context, **kwargs) 802 803def use_cython_array_utility_code(env): 804 cython_scope = env.global_scope().context.cython_scope 805 cython_scope.load_cythonscope() 806 cython_scope.viewscope.lookup('array_cwrapper').used = True 807 808context = { 809 'memview_struct_name': memview_objstruct_cname, 810 'max_dims': Options.buffer_max_dims, 811 'memviewslice_name': memviewslice_cname, 812 'memslice_init': memslice_entry_init, 813} 814memviewslice_declare_code = load_memview_c_utility( 815 "MemviewSliceStruct", 816 context=context, 817 requires=[]) 818 819atomic_utility = load_memview_c_utility("Atomics", context) 820 821memviewslice_init_code = load_memview_c_utility( 822 "MemviewSliceInit", 823 context=dict(context, BUF_MAX_NDIMS=Options.buffer_max_dims), 824 requires=[memviewslice_declare_code, 825 atomic_utility], 826) 827 828memviewslice_index_helpers = load_memview_c_utility("MemviewSliceIndex") 829 830typeinfo_to_format_code = load_memview_cy_utility( 831 "BufferFormatFromTypeInfo", requires=[Buffer._typeinfo_to_format_code]) 832 833is_contig_utility = load_memview_c_utility("MemviewSliceIsContig", context) 834overlapping_utility = load_memview_c_utility("OverlappingSlices", context) 835copy_contents_new_utility = load_memview_c_utility( 836 "MemviewSliceCopyTemplate", 837 context, 838 requires=[], # require cython_array_utility_code 839) 840 841view_utility_code = load_memview_cy_utility( 842 "View.MemoryView", 843 context=context, 844 requires=[Buffer.GetAndReleaseBufferUtilityCode(), 845 Buffer.buffer_struct_declare_code, 846 Buffer.buffer_formats_declare_code, 847 memviewslice_init_code, 848 is_contig_utility, 849 overlapping_utility, 850 copy_contents_new_utility, 851 ModuleNode.capsule_utility_code], 852) 853view_utility_whitelist = ('array', 'memoryview', 'array_cwrapper', 854 'generic', 'strided', 'indirect', 'contiguous', 855 'indirect_contiguous') 856 857memviewslice_declare_code.requires.append(view_utility_code) 858copy_contents_new_utility.requires.append(view_utility_code) 859