1from __future__ import absolute_import 2 3from .Visitor import CythonTransform 4from .ModuleNode import ModuleNode 5from .Errors import CompileError 6from .UtilityCode import CythonUtilityCode 7from .Code import UtilityCode, TempitaUtilityCode 8 9from . import Options 10from . import Interpreter 11from . import PyrexTypes 12from . import Naming 13from . import Symtab 14 15def dedent(text, reindent=0): 16 from textwrap import dedent 17 text = dedent(text) 18 if reindent > 0: 19 indent = " " * reindent 20 text = '\n'.join([indent + x for x in text.split('\n')]) 21 return text 22 23class IntroduceBufferAuxiliaryVars(CythonTransform): 24 25 # 26 # Entry point 27 # 28 29 buffers_exists = False 30 using_memoryview = False 31 32 def __call__(self, node): 33 assert isinstance(node, ModuleNode) 34 self.max_ndim = 0 35 result = super(IntroduceBufferAuxiliaryVars, self).__call__(node) 36 if self.buffers_exists: 37 use_bufstruct_declare_code(node.scope) 38 use_py2_buffer_functions(node.scope) 39 40 return result 41 42 43 # 44 # Basic operations for transforms 45 # 46 def handle_scope(self, node, scope): 47 # For all buffers, insert extra variables in the scope. 48 # The variables are also accessible from the buffer_info 49 # on the buffer entry 50 scope_items = scope.entries.items() 51 bufvars = [entry for name, entry in scope_items if entry.type.is_buffer] 52 if len(bufvars) > 0: 53 bufvars.sort(key=lambda entry: entry.name) 54 self.buffers_exists = True 55 56 memviewslicevars = [entry for name, entry in scope_items if entry.type.is_memoryviewslice] 57 if len(memviewslicevars) > 0: 58 self.buffers_exists = True 59 60 61 for (name, entry) in scope_items: 62 if name == 'memoryview' and isinstance(entry.utility_code_definition, CythonUtilityCode): 63 self.using_memoryview = True 64 break 65 del scope_items 66 67 if isinstance(node, ModuleNode) and len(bufvars) > 0: 68 # for now...note that pos is wrong 69 raise CompileError(node.pos, "Buffer vars not allowed in module scope") 70 for entry in bufvars: 71 if entry.type.dtype.is_ptr: 72 raise CompileError(node.pos, "Buffers with pointer types not yet supported.") 73 74 name = entry.name 75 buftype = entry.type 76 if buftype.ndim > Options.buffer_max_dims: 77 raise CompileError(node.pos, 78 "Buffer ndims exceeds Options.buffer_max_dims = %d" % Options.buffer_max_dims) 79 if buftype.ndim > self.max_ndim: 80 self.max_ndim = buftype.ndim 81 82 # Declare auxiliary vars 83 def decvar(type, prefix): 84 cname = scope.mangle(prefix, name) 85 aux_var = scope.declare_var(name=None, cname=cname, 86 type=type, pos=node.pos) 87 if entry.is_arg: 88 aux_var.used = True # otherwise, NameNode will mark whether it is used 89 90 return aux_var 91 92 auxvars = ((PyrexTypes.c_pyx_buffer_nd_type, Naming.pybuffernd_prefix), 93 (PyrexTypes.c_pyx_buffer_type, Naming.pybufferstruct_prefix)) 94 pybuffernd, rcbuffer = [decvar(type, prefix) for (type, prefix) in auxvars] 95 96 entry.buffer_aux = Symtab.BufferAux(pybuffernd, rcbuffer) 97 98 scope.buffer_entries = bufvars 99 self.scope = scope 100 101 def visit_ModuleNode(self, node): 102 self.handle_scope(node, node.scope) 103 self.visitchildren(node) 104 return node 105 106 def visit_FuncDefNode(self, node): 107 self.handle_scope(node, node.local_scope) 108 self.visitchildren(node) 109 return node 110 111# 112# Analysis 113# 114buffer_options = ("dtype", "ndim", "mode", "negative_indices", "cast") # ordered! 115buffer_defaults = {"ndim": 1, "mode": "full", "negative_indices": True, "cast": False} 116buffer_positional_options_count = 1 # anything beyond this needs keyword argument 117 118ERR_BUF_OPTION_UNKNOWN = '"%s" is not a buffer option' 119ERR_BUF_TOO_MANY = 'Too many buffer options' 120ERR_BUF_DUP = '"%s" buffer option already supplied' 121ERR_BUF_MISSING = '"%s" missing' 122ERR_BUF_MODE = 'Only allowed buffer modes are: "c", "fortran", "full", "strided" (as a compile-time string)' 123ERR_BUF_NDIM = 'ndim must be a non-negative integer' 124ERR_BUF_DTYPE = 'dtype must be "object", numeric type or a struct' 125ERR_BUF_BOOL = '"%s" must be a boolean' 126 127def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, need_complete=True): 128 """ 129 Must be called during type analysis, as analyse is called 130 on the dtype argument. 131 132 posargs and dictargs should consist of a list and a dict 133 of tuples (value, pos). Defaults should be a dict of values. 134 135 Returns a dict containing all the options a buffer can have and 136 its value (with the positions stripped). 137 """ 138 if defaults is None: 139 defaults = buffer_defaults 140 141 posargs, dictargs = Interpreter.interpret_compiletime_options( 142 posargs, dictargs, type_env=env, type_args=(0, 'dtype')) 143 144 if len(posargs) > buffer_positional_options_count: 145 raise CompileError(posargs[-1][1], ERR_BUF_TOO_MANY) 146 147 options = {} 148 for name, (value, pos) in dictargs.items(): 149 if not name in buffer_options: 150 raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name) 151 options[name] = value 152 153 for name, (value, pos) in zip(buffer_options, posargs): 154 if not name in buffer_options: 155 raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name) 156 if name in options: 157 raise CompileError(pos, ERR_BUF_DUP % name) 158 options[name] = value 159 160 # Check that they are all there and copy defaults 161 for name in buffer_options: 162 if not name in options: 163 try: 164 options[name] = defaults[name] 165 except KeyError: 166 if need_complete: 167 raise CompileError(globalpos, ERR_BUF_MISSING % name) 168 169 dtype = options.get("dtype") 170 if dtype and dtype.is_extension_type: 171 raise CompileError(globalpos, ERR_BUF_DTYPE) 172 173 ndim = options.get("ndim") 174 if ndim and (not isinstance(ndim, int) or ndim < 0): 175 raise CompileError(globalpos, ERR_BUF_NDIM) 176 177 mode = options.get("mode") 178 if mode and not (mode in ('full', 'strided', 'c', 'fortran')): 179 raise CompileError(globalpos, ERR_BUF_MODE) 180 181 def assert_bool(name): 182 x = options.get(name) 183 if not isinstance(x, bool): 184 raise CompileError(globalpos, ERR_BUF_BOOL % name) 185 186 assert_bool('negative_indices') 187 assert_bool('cast') 188 189 return options 190 191 192# 193# Code generation 194# 195 196class BufferEntry(object): 197 def __init__(self, entry): 198 self.entry = entry 199 self.type = entry.type 200 self.cname = entry.buffer_aux.buflocal_nd_var.cname 201 self.buf_ptr = "%s.rcbuffer->pybuffer.buf" % self.cname 202 self.buf_ptr_type = entry.type.buffer_ptr_type 203 self.init_attributes() 204 205 def init_attributes(self): 206 self.shape = self.get_buf_shapevars() 207 self.strides = self.get_buf_stridevars() 208 self.suboffsets = self.get_buf_suboffsetvars() 209 210 def get_buf_suboffsetvars(self): 211 return self._for_all_ndim("%s.diminfo[%d].suboffsets") 212 213 def get_buf_stridevars(self): 214 return self._for_all_ndim("%s.diminfo[%d].strides") 215 216 def get_buf_shapevars(self): 217 return self._for_all_ndim("%s.diminfo[%d].shape") 218 219 def _for_all_ndim(self, s): 220 return [s % (self.cname, i) for i in range(self.type.ndim)] 221 222 def generate_buffer_lookup_code(self, code, index_cnames): 223 # Create buffer lookup and return it 224 # This is done via utility macros/inline functions, which vary 225 # according to the access mode used. 226 params = [] 227 nd = self.type.ndim 228 mode = self.type.mode 229 if mode == 'full': 230 for i, s, o in zip(index_cnames, 231 self.get_buf_stridevars(), 232 self.get_buf_suboffsetvars()): 233 params.append(i) 234 params.append(s) 235 params.append(o) 236 funcname = "__Pyx_BufPtrFull%dd" % nd 237 funcgen = buf_lookup_full_code 238 else: 239 if mode == 'strided': 240 funcname = "__Pyx_BufPtrStrided%dd" % nd 241 funcgen = buf_lookup_strided_code 242 elif mode == 'c': 243 funcname = "__Pyx_BufPtrCContig%dd" % nd 244 funcgen = buf_lookup_c_code 245 elif mode == 'fortran': 246 funcname = "__Pyx_BufPtrFortranContig%dd" % nd 247 funcgen = buf_lookup_fortran_code 248 else: 249 assert False 250 for i, s in zip(index_cnames, self.get_buf_stridevars()): 251 params.append(i) 252 params.append(s) 253 254 # Make sure the utility code is available 255 if funcname not in code.globalstate.utility_codes: 256 code.globalstate.utility_codes.add(funcname) 257 protocode = code.globalstate['utility_code_proto'] 258 defcode = code.globalstate['utility_code_def'] 259 funcgen(protocode, defcode, name=funcname, nd=nd) 260 261 buf_ptr_type_code = self.buf_ptr_type.empty_declaration_code() 262 ptrcode = "%s(%s, %s, %s)" % (funcname, buf_ptr_type_code, self.buf_ptr, 263 ", ".join(params)) 264 return ptrcode 265 266 267def get_flags(buffer_aux, buffer_type): 268 flags = 'PyBUF_FORMAT' 269 mode = buffer_type.mode 270 if mode == 'full': 271 flags += '| PyBUF_INDIRECT' 272 elif mode == 'strided': 273 flags += '| PyBUF_STRIDES' 274 elif mode == 'c': 275 flags += '| PyBUF_C_CONTIGUOUS' 276 elif mode == 'fortran': 277 flags += '| PyBUF_F_CONTIGUOUS' 278 else: 279 assert False 280 if buffer_aux.writable_needed: flags += "| PyBUF_WRITABLE" 281 return flags 282 283def used_buffer_aux_vars(entry): 284 buffer_aux = entry.buffer_aux 285 buffer_aux.buflocal_nd_var.used = True 286 buffer_aux.rcbuf_var.used = True 287 288def put_unpack_buffer_aux_into_scope(buf_entry, code): 289 # Generate code to copy the needed struct info into local 290 # variables. 291 buffer_aux, mode = buf_entry.buffer_aux, buf_entry.type.mode 292 pybuffernd_struct = buffer_aux.buflocal_nd_var.cname 293 294 fldnames = ['strides', 'shape'] 295 if mode == 'full': 296 fldnames.append('suboffsets') 297 298 ln = [] 299 for i in range(buf_entry.type.ndim): 300 for fldname in fldnames: 301 ln.append("%s.diminfo[%d].%s = %s.rcbuffer->pybuffer.%s[%d];" % \ 302 (pybuffernd_struct, i, fldname, 303 pybuffernd_struct, fldname, i)) 304 code.putln(' '.join(ln)) 305 306def put_init_vars(entry, code): 307 bufaux = entry.buffer_aux 308 pybuffernd_struct = bufaux.buflocal_nd_var.cname 309 pybuffer_struct = bufaux.rcbuf_var.cname 310 # init pybuffer_struct 311 code.putln("%s.pybuffer.buf = NULL;" % pybuffer_struct) 312 code.putln("%s.refcount = 0;" % pybuffer_struct) 313 # init the buffer object 314 # code.put_init_var_to_py_none(entry) 315 # init the pybuffernd_struct 316 code.putln("%s.data = NULL;" % pybuffernd_struct) 317 code.putln("%s.rcbuffer = &%s;" % (pybuffernd_struct, pybuffer_struct)) 318 319 320def put_acquire_arg_buffer(entry, code, pos): 321 buffer_aux = entry.buffer_aux 322 getbuffer = get_getbuffer_call(code, entry.cname, buffer_aux, entry.type) 323 324 # Acquire any new buffer 325 code.putln("{") 326 code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % entry.type.dtype.struct_nesting_depth()) 327 code.putln(code.error_goto_if("%s == -1" % getbuffer, pos)) 328 code.putln("}") 329 # An exception raised in arg parsing cannot be caught, so no 330 # need to care about the buffer then. 331 put_unpack_buffer_aux_into_scope(entry, code) 332 333 334def put_release_buffer_code(code, entry): 335 code.globalstate.use_utility_code(acquire_utility_code) 336 code.putln("__Pyx_SafeReleaseBuffer(&%s.rcbuffer->pybuffer);" % entry.buffer_aux.buflocal_nd_var.cname) 337 338 339def get_getbuffer_call(code, obj_cname, buffer_aux, buffer_type): 340 ndim = buffer_type.ndim 341 cast = int(buffer_type.cast) 342 flags = get_flags(buffer_aux, buffer_type) 343 pybuffernd_struct = buffer_aux.buflocal_nd_var.cname 344 345 dtype_typeinfo = get_type_information_cname(code, buffer_type.dtype) 346 347 code.globalstate.use_utility_code(acquire_utility_code) 348 return ("__Pyx_GetBufferAndValidate(&%(pybuffernd_struct)s.rcbuffer->pybuffer, " 349 "(PyObject*)%(obj_cname)s, &%(dtype_typeinfo)s, %(flags)s, %(ndim)d, " 350 "%(cast)d, __pyx_stack)" % locals()) 351 352 353def put_assign_to_buffer(lhs_cname, rhs_cname, buf_entry, 354 is_initialized, pos, code): 355 """ 356 Generate code for reassigning a buffer variables. This only deals with getting 357 the buffer auxiliary structure and variables set up correctly, the assignment 358 itself and refcounting is the responsibility of the caller. 359 360 However, the assignment operation may throw an exception so that the reassignment 361 never happens. 362 363 Depending on the circumstances there are two possible outcomes: 364 - Old buffer released, new acquired, rhs assigned to lhs 365 - Old buffer released, new acquired which fails, reaqcuire old lhs buffer 366 (which may or may not succeed). 367 """ 368 369 buffer_aux, buffer_type = buf_entry.buffer_aux, buf_entry.type 370 pybuffernd_struct = buffer_aux.buflocal_nd_var.cname 371 flags = get_flags(buffer_aux, buffer_type) 372 373 code.putln("{") # Set up necessary stack for getbuffer 374 code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % buffer_type.dtype.struct_nesting_depth()) 375 376 getbuffer = get_getbuffer_call(code, "%s", buffer_aux, buffer_type) # fill in object below 377 378 if is_initialized: 379 # Release any existing buffer 380 code.putln('__Pyx_SafeReleaseBuffer(&%s.rcbuffer->pybuffer);' % pybuffernd_struct) 381 # Acquire 382 retcode_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False) 383 code.putln("%s = %s;" % (retcode_cname, getbuffer % rhs_cname)) 384 code.putln('if (%s) {' % (code.unlikely("%s < 0" % retcode_cname))) 385 # If acquisition failed, attempt to reacquire the old buffer 386 # before raising the exception. A failure of reacquisition 387 # will cause the reacquisition exception to be reported, one 388 # can consider working around this later. 389 exc_temps = tuple(code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=False) 390 for _ in range(3)) 391 code.putln('PyErr_Fetch(&%s, &%s, &%s);' % exc_temps) 392 code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % lhs_cname))) 393 code.putln('Py_XDECREF(%s); Py_XDECREF(%s); Py_XDECREF(%s);' % exc_temps) # Do not refnanny these! 394 code.globalstate.use_utility_code(raise_buffer_fallback_code) 395 code.putln('__Pyx_RaiseBufferFallbackError();') 396 code.putln('} else {') 397 code.putln('PyErr_Restore(%s, %s, %s);' % exc_temps) 398 code.putln('}') 399 code.putln('%s = %s = %s = 0;' % exc_temps) 400 for t in exc_temps: 401 code.funcstate.release_temp(t) 402 code.putln('}') 403 # Unpack indices 404 put_unpack_buffer_aux_into_scope(buf_entry, code) 405 code.putln(code.error_goto_if_neg(retcode_cname, pos)) 406 code.funcstate.release_temp(retcode_cname) 407 else: 408 # Our entry had no previous value, so set to None when acquisition fails. 409 # In this case, auxiliary vars should be set up right in initialization to a zero-buffer, 410 # so it suffices to set the buf field to NULL. 411 code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % rhs_cname))) 412 code.putln('%s = %s; __Pyx_INCREF(Py_None); %s.rcbuffer->pybuffer.buf = NULL;' % 413 (lhs_cname, 414 PyrexTypes.typecast(buffer_type, PyrexTypes.py_object_type, "Py_None"), 415 pybuffernd_struct)) 416 code.putln(code.error_goto(pos)) 417 code.put('} else {') 418 # Unpack indices 419 put_unpack_buffer_aux_into_scope(buf_entry, code) 420 code.putln('}') 421 422 code.putln("}") # Release stack 423 424 425def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives, 426 pos, code, negative_indices, in_nogil_context): 427 """ 428 Generates code to process indices and calculate an offset into 429 a buffer. Returns a C string which gives a pointer which can be 430 read from or written to at will (it is an expression so caller should 431 store it in a temporary if it is used more than once). 432 433 As the bounds checking can have any number of combinations of unsigned 434 arguments, smart optimizations etc. we insert it directly in the function 435 body. The lookup however is delegated to a inline function that is instantiated 436 once per ndim (lookup with suboffsets tend to get quite complicated). 437 438 entry is a BufferEntry 439 """ 440 negative_indices = directives['wraparound'] and negative_indices 441 442 if directives['boundscheck']: 443 # Check bounds and fix negative indices. 444 # We allocate a temporary which is initialized to -1, meaning OK (!). 445 # If an error occurs, the temp is set to the index dimension the 446 # error is occurring at. 447 failed_dim_temp = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False) 448 code.putln("%s = -1;" % failed_dim_temp) 449 for dim, (signed, cname, shape) in enumerate(zip(index_signeds, index_cnames, entry.get_buf_shapevars())): 450 if signed != 0: 451 # not unsigned, deal with negative index 452 code.putln("if (%s < 0) {" % cname) 453 if negative_indices: 454 code.putln("%s += %s;" % (cname, shape)) 455 code.putln("if (%s) %s = %d;" % ( 456 code.unlikely("%s < 0" % cname), 457 failed_dim_temp, dim)) 458 else: 459 code.putln("%s = %d;" % (failed_dim_temp, dim)) 460 code.put("} else ") 461 # check bounds in positive direction 462 if signed != 0: 463 cast = "" 464 else: 465 cast = "(size_t)" 466 code.putln("if (%s) %s = %d;" % ( 467 code.unlikely("%s >= %s%s" % (cname, cast, shape)), 468 failed_dim_temp, dim)) 469 470 if in_nogil_context: 471 code.globalstate.use_utility_code(raise_indexerror_nogil) 472 func = '__Pyx_RaiseBufferIndexErrorNogil' 473 else: 474 code.globalstate.use_utility_code(raise_indexerror_code) 475 func = '__Pyx_RaiseBufferIndexError' 476 477 code.putln("if (%s) {" % code.unlikely("%s != -1" % failed_dim_temp)) 478 code.putln('%s(%s);' % (func, failed_dim_temp)) 479 code.putln(code.error_goto(pos)) 480 code.putln('}') 481 code.funcstate.release_temp(failed_dim_temp) 482 elif negative_indices: 483 # Only fix negative indices. 484 for signed, cname, shape in zip(index_signeds, index_cnames, entry.get_buf_shapevars()): 485 if signed != 0: 486 code.putln("if (%s < 0) %s += %s;" % (cname, cname, shape)) 487 488 return entry.generate_buffer_lookup_code(code, index_cnames) 489 490 491def use_bufstruct_declare_code(env): 492 env.use_utility_code(buffer_struct_declare_code) 493 494 495def buf_lookup_full_code(proto, defin, name, nd): 496 """ 497 Generates a buffer lookup function for the right number 498 of dimensions. The function gives back a void* at the right location. 499 """ 500 # _i_ndex, _s_tride, sub_o_ffset 501 macroargs = ", ".join(["i%d, s%d, o%d" % (i, i, i) for i in range(nd)]) 502 proto.putln("#define %s(type, buf, %s) (type)(%s_imp(buf, %s))" % (name, macroargs, name, macroargs)) 503 504 funcargs = ", ".join(["Py_ssize_t i%d, Py_ssize_t s%d, Py_ssize_t o%d" % (i, i, i) for i in range(nd)]) 505 proto.putln("static CYTHON_INLINE void* %s_imp(void* buf, %s);" % (name, funcargs)) 506 defin.putln(dedent(""" 507 static CYTHON_INLINE void* %s_imp(void* buf, %s) { 508 char* ptr = (char*)buf; 509 """) % (name, funcargs) + "".join([dedent("""\ 510 ptr += s%d * i%d; 511 if (o%d >= 0) ptr = *((char**)ptr) + o%d; 512 """) % (i, i, i, i) for i in range(nd)] 513 ) + "\nreturn ptr;\n}") 514 515 516def buf_lookup_strided_code(proto, defin, name, nd): 517 """ 518 Generates a buffer lookup function for the right number 519 of dimensions. The function gives back a void* at the right location. 520 """ 521 # _i_ndex, _s_tride 522 args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)]) 523 offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd)]) 524 proto.putln("#define %s(type, buf, %s) (type)((char*)buf + %s)" % (name, args, offset)) 525 526 527def buf_lookup_c_code(proto, defin, name, nd): 528 """ 529 Similar to strided lookup, but can assume that the last dimension 530 doesn't need a multiplication as long as. 531 Still we keep the same signature for now. 532 """ 533 if nd == 1: 534 proto.putln("#define %s(type, buf, i0, s0) ((type)buf + i0)" % name) 535 else: 536 args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)]) 537 offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd - 1)]) 538 proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, nd - 1)) 539 540 541def buf_lookup_fortran_code(proto, defin, name, nd): 542 """ 543 Like C lookup, but the first index is optimized instead. 544 """ 545 if nd == 1: 546 proto.putln("#define %s(type, buf, i0, s0) ((type)buf + i0)" % name) 547 else: 548 args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)]) 549 offset = " + ".join(["i%d * s%d" % (i, i) for i in range(1, nd)]) 550 proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, 0)) 551 552 553def use_py2_buffer_functions(env): 554 env.use_utility_code(GetAndReleaseBufferUtilityCode()) 555 556 557class GetAndReleaseBufferUtilityCode(object): 558 # Emulation of PyObject_GetBuffer and PyBuffer_Release for Python 2. 559 # For >= 2.6 we do double mode -- use the new buffer interface on objects 560 # which has the right tp_flags set, but emulation otherwise. 561 562 requires = None 563 is_cython_utility = False 564 565 def __init__(self): 566 pass 567 568 def __eq__(self, other): 569 return isinstance(other, GetAndReleaseBufferUtilityCode) 570 571 def __hash__(self): 572 return 24342342 573 574 def get_tree(self, **kwargs): pass 575 576 def put_code(self, output): 577 code = output['utility_code_def'] 578 proto_code = output['utility_code_proto'] 579 env = output.module_node.scope 580 cython_scope = env.context.cython_scope 581 582 # Search all types for __getbuffer__ overloads 583 types = [] 584 visited_scopes = set() 585 def find_buffer_types(scope): 586 if scope in visited_scopes: 587 return 588 visited_scopes.add(scope) 589 for m in scope.cimported_modules: 590 find_buffer_types(m) 591 for e in scope.type_entries: 592 if isinstance(e.utility_code_definition, CythonUtilityCode): 593 continue 594 t = e.type 595 if t.is_extension_type: 596 if scope is cython_scope and not e.used: 597 continue 598 release = get = None 599 for x in t.scope.pyfunc_entries: 600 if x.name == u"__getbuffer__": get = x.func_cname 601 elif x.name == u"__releasebuffer__": release = x.func_cname 602 if get: 603 types.append((t.typeptr_cname, get, release)) 604 605 find_buffer_types(env) 606 607 util_code = TempitaUtilityCode.load( 608 "GetAndReleaseBuffer", from_file="Buffer.c", 609 context=dict(types=types)) 610 611 proto = util_code.format_code(util_code.proto) 612 impl = util_code.format_code( 613 util_code.inject_string_constants(util_code.impl, output)[1]) 614 615 proto_code.putln(proto) 616 code.putln(impl) 617 618 619def mangle_dtype_name(dtype): 620 # Use prefixes to separate user defined types from builtins 621 # (consider "typedef float unsigned_int") 622 if dtype.is_pyobject: 623 return "object" 624 elif dtype.is_ptr: 625 return "ptr" 626 else: 627 if dtype.is_typedef or dtype.is_struct_or_union: 628 prefix = "nn_" 629 else: 630 prefix = "" 631 return prefix + dtype.specialization_name() 632 633def get_type_information_cname(code, dtype, maxdepth=None): 634 """ 635 Output the run-time type information (__Pyx_TypeInfo) for given dtype, 636 and return the name of the type info struct. 637 638 Structs with two floats of the same size are encoded as complex numbers. 639 One can separate between complex numbers declared as struct or with native 640 encoding by inspecting to see if the fields field of the type is 641 filled in. 642 """ 643 namesuffix = mangle_dtype_name(dtype) 644 name = "__Pyx_TypeInfo_%s" % namesuffix 645 structinfo_name = "__Pyx_StructFields_%s" % namesuffix 646 647 if dtype.is_error: return "<error>" 648 649 # It's critical that walking the type info doesn't use more stack 650 # depth than dtype.struct_nesting_depth() returns, so use an assertion for this 651 if maxdepth is None: maxdepth = dtype.struct_nesting_depth() 652 if maxdepth <= 0: 653 assert False 654 655 if name not in code.globalstate.utility_codes: 656 code.globalstate.utility_codes.add(name) 657 typecode = code.globalstate['typeinfo'] 658 659 arraysizes = [] 660 if dtype.is_array: 661 while dtype.is_array: 662 arraysizes.append(dtype.size) 663 dtype = dtype.base_type 664 665 complex_possible = dtype.is_struct_or_union and dtype.can_be_complex() 666 667 declcode = dtype.empty_declaration_code() 668 if dtype.is_simple_buffer_dtype(): 669 structinfo_name = "NULL" 670 elif dtype.is_struct: 671 struct_scope = dtype.scope 672 if dtype.is_const: 673 struct_scope = struct_scope.const_base_type_scope 674 # Must pre-call all used types in order not to recurse during utility code writing. 675 fields = struct_scope.var_entries 676 assert len(fields) > 0 677 types = [get_type_information_cname(code, f.type, maxdepth - 1) 678 for f in fields] 679 typecode.putln("static __Pyx_StructField %s[] = {" % structinfo_name, safe=True) 680 for f, typeinfo in zip(fields, types): 681 typecode.putln(' {&%s, "%s", offsetof(%s, %s)},' % 682 (typeinfo, f.name, dtype.empty_declaration_code(), f.cname), safe=True) 683 typecode.putln(' {NULL, NULL, 0}', safe=True) 684 typecode.putln("};", safe=True) 685 else: 686 assert False 687 688 rep = str(dtype) 689 690 flags = "0" 691 is_unsigned = "0" 692 if dtype is PyrexTypes.c_char_type: 693 is_unsigned = "IS_UNSIGNED(%s)" % declcode 694 typegroup = "'H'" 695 elif dtype.is_int: 696 is_unsigned = "IS_UNSIGNED(%s)" % declcode 697 typegroup = "%s ? 'U' : 'I'" % is_unsigned 698 elif complex_possible or dtype.is_complex: 699 typegroup = "'C'" 700 elif dtype.is_float: 701 typegroup = "'R'" 702 elif dtype.is_struct: 703 typegroup = "'S'" 704 if dtype.packed: 705 flags = "__PYX_BUF_FLAGS_PACKED_STRUCT" 706 elif dtype.is_pyobject: 707 typegroup = "'O'" 708 else: 709 assert False, dtype 710 711 typeinfo = ('static __Pyx_TypeInfo %s = ' 712 '{ "%s", %s, sizeof(%s), { %s }, %s, %s, %s, %s };') 713 tup = (name, rep, structinfo_name, declcode, 714 ', '.join([str(x) for x in arraysizes]) or '0', len(arraysizes), 715 typegroup, is_unsigned, flags) 716 typecode.putln(typeinfo % tup, safe=True) 717 718 return name 719 720def load_buffer_utility(util_code_name, context=None, **kwargs): 721 if context is None: 722 return UtilityCode.load(util_code_name, "Buffer.c", **kwargs) 723 else: 724 return TempitaUtilityCode.load(util_code_name, "Buffer.c", context=context, **kwargs) 725 726context = dict(max_dims=Options.buffer_max_dims) 727buffer_struct_declare_code = load_buffer_utility("BufferStructDeclare", context=context) 728buffer_formats_declare_code = load_buffer_utility("BufferFormatStructs") 729 730# Utility function to set the right exception 731# The caller should immediately goto_error 732raise_indexerror_code = load_buffer_utility("BufferIndexError") 733raise_indexerror_nogil = load_buffer_utility("BufferIndexErrorNogil") 734raise_buffer_fallback_code = load_buffer_utility("BufferFallbackError") 735 736acquire_utility_code = load_buffer_utility("BufferGetAndValidate", context=context) 737buffer_format_check_code = load_buffer_utility("BufferFormatCheck", context=context) 738 739# See utility code BufferFormatFromTypeInfo 740_typeinfo_to_format_code = load_buffer_utility("TypeInfoToFormat") 741