123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740 |
- from __future__ import absolute_import
- from .Visitor import CythonTransform
- from .ModuleNode import ModuleNode
- from .Errors import CompileError
- from .UtilityCode import CythonUtilityCode
- from .Code import UtilityCode, TempitaUtilityCode
- from . import Options
- from . import Interpreter
- from . import PyrexTypes
- from . import Naming
- from . import Symtab
- def dedent(text, reindent=0):
- from textwrap import dedent
- text = dedent(text)
- if reindent > 0:
- indent = " " * reindent
- text = '\n'.join([indent + x for x in text.split('\n')])
- return text
- class IntroduceBufferAuxiliaryVars(CythonTransform):
- #
- # Entry point
- #
- buffers_exists = False
- using_memoryview = False
- def __call__(self, node):
- assert isinstance(node, ModuleNode)
- self.max_ndim = 0
- result = super(IntroduceBufferAuxiliaryVars, self).__call__(node)
- if self.buffers_exists:
- use_bufstruct_declare_code(node.scope)
- use_py2_buffer_functions(node.scope)
- return result
- #
- # Basic operations for transforms
- #
- def handle_scope(self, node, scope):
- # For all buffers, insert extra variables in the scope.
- # The variables are also accessible from the buffer_info
- # on the buffer entry
- scope_items = scope.entries.items()
- bufvars = [entry for name, entry in scope_items if entry.type.is_buffer]
- if len(bufvars) > 0:
- bufvars.sort(key=lambda entry: entry.name)
- self.buffers_exists = True
- memviewslicevars = [entry for name, entry in scope_items if entry.type.is_memoryviewslice]
- if len(memviewslicevars) > 0:
- self.buffers_exists = True
- for (name, entry) in scope_items:
- if name == 'memoryview' and isinstance(entry.utility_code_definition, CythonUtilityCode):
- self.using_memoryview = True
- break
- del scope_items
- if isinstance(node, ModuleNode) and len(bufvars) > 0:
- # for now...note that pos is wrong
- raise CompileError(node.pos, "Buffer vars not allowed in module scope")
- for entry in bufvars:
- if entry.type.dtype.is_ptr:
- raise CompileError(node.pos, "Buffers with pointer types not yet supported.")
- name = entry.name
- buftype = entry.type
- if buftype.ndim > Options.buffer_max_dims:
- raise CompileError(node.pos,
- "Buffer ndims exceeds Options.buffer_max_dims = %d" % Options.buffer_max_dims)
- if buftype.ndim > self.max_ndim:
- self.max_ndim = buftype.ndim
- # Declare auxiliary vars
- def decvar(type, prefix):
- cname = scope.mangle(prefix, name)
- aux_var = scope.declare_var(name=None, cname=cname,
- type=type, pos=node.pos)
- if entry.is_arg:
- aux_var.used = True # otherwise, NameNode will mark whether it is used
- return aux_var
- auxvars = ((PyrexTypes.c_pyx_buffer_nd_type, Naming.pybuffernd_prefix),
- (PyrexTypes.c_pyx_buffer_type, Naming.pybufferstruct_prefix))
- pybuffernd, rcbuffer = [decvar(type, prefix) for (type, prefix) in auxvars]
- entry.buffer_aux = Symtab.BufferAux(pybuffernd, rcbuffer)
- scope.buffer_entries = bufvars
- self.scope = scope
- def visit_ModuleNode(self, node):
- self.handle_scope(node, node.scope)
- self.visitchildren(node)
- return node
- def visit_FuncDefNode(self, node):
- self.handle_scope(node, node.local_scope)
- self.visitchildren(node)
- return node
- #
- # Analysis
- #
- buffer_options = ("dtype", "ndim", "mode", "negative_indices", "cast") # ordered!
- buffer_defaults = {"ndim": 1, "mode": "full", "negative_indices": True, "cast": False}
- buffer_positional_options_count = 1 # anything beyond this needs keyword argument
- ERR_BUF_OPTION_UNKNOWN = '"%s" is not a buffer option'
- ERR_BUF_TOO_MANY = 'Too many buffer options'
- ERR_BUF_DUP = '"%s" buffer option already supplied'
- ERR_BUF_MISSING = '"%s" missing'
- ERR_BUF_MODE = 'Only allowed buffer modes are: "c", "fortran", "full", "strided" (as a compile-time string)'
- ERR_BUF_NDIM = 'ndim must be a non-negative integer'
- ERR_BUF_DTYPE = 'dtype must be "object", numeric type or a struct'
- ERR_BUF_BOOL = '"%s" must be a boolean'
- def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, need_complete=True):
- """
- Must be called during type analysis, as analyse is called
- on the dtype argument.
- posargs and dictargs should consist of a list and a dict
- of tuples (value, pos). Defaults should be a dict of values.
- Returns a dict containing all the options a buffer can have and
- its value (with the positions stripped).
- """
- if defaults is None:
- defaults = buffer_defaults
- posargs, dictargs = Interpreter.interpret_compiletime_options(
- posargs, dictargs, type_env=env, type_args=(0, 'dtype'))
- if len(posargs) > buffer_positional_options_count:
- raise CompileError(posargs[-1][1], ERR_BUF_TOO_MANY)
- options = {}
- for name, (value, pos) in dictargs.items():
- if not name in buffer_options:
- raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name)
- options[name] = value
- for name, (value, pos) in zip(buffer_options, posargs):
- if not name in buffer_options:
- raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name)
- if name in options:
- raise CompileError(pos, ERR_BUF_DUP % name)
- options[name] = value
- # Check that they are all there and copy defaults
- for name in buffer_options:
- if not name in options:
- try:
- options[name] = defaults[name]
- except KeyError:
- if need_complete:
- raise CompileError(globalpos, ERR_BUF_MISSING % name)
- dtype = options.get("dtype")
- if dtype and dtype.is_extension_type:
- raise CompileError(globalpos, ERR_BUF_DTYPE)
- ndim = options.get("ndim")
- if ndim and (not isinstance(ndim, int) or ndim < 0):
- raise CompileError(globalpos, ERR_BUF_NDIM)
- mode = options.get("mode")
- if mode and not (mode in ('full', 'strided', 'c', 'fortran')):
- raise CompileError(globalpos, ERR_BUF_MODE)
- def assert_bool(name):
- x = options.get(name)
- if not isinstance(x, bool):
- raise CompileError(globalpos, ERR_BUF_BOOL % name)
- assert_bool('negative_indices')
- assert_bool('cast')
- return options
- #
- # Code generation
- #
- class BufferEntry(object):
- def __init__(self, entry):
- self.entry = entry
- self.type = entry.type
- self.cname = entry.buffer_aux.buflocal_nd_var.cname
- self.buf_ptr = "%s.rcbuffer->pybuffer.buf" % self.cname
- self.buf_ptr_type = entry.type.buffer_ptr_type
- self.init_attributes()
- def init_attributes(self):
- self.shape = self.get_buf_shapevars()
- self.strides = self.get_buf_stridevars()
- self.suboffsets = self.get_buf_suboffsetvars()
- def get_buf_suboffsetvars(self):
- return self._for_all_ndim("%s.diminfo[%d].suboffsets")
- def get_buf_stridevars(self):
- return self._for_all_ndim("%s.diminfo[%d].strides")
- def get_buf_shapevars(self):
- return self._for_all_ndim("%s.diminfo[%d].shape")
- def _for_all_ndim(self, s):
- return [s % (self.cname, i) for i in range(self.type.ndim)]
- def generate_buffer_lookup_code(self, code, index_cnames):
- # Create buffer lookup and return it
- # This is done via utility macros/inline functions, which vary
- # according to the access mode used.
- params = []
- nd = self.type.ndim
- mode = self.type.mode
- if mode == 'full':
- for i, s, o in zip(index_cnames,
- self.get_buf_stridevars(),
- self.get_buf_suboffsetvars()):
- params.append(i)
- params.append(s)
- params.append(o)
- funcname = "__Pyx_BufPtrFull%dd" % nd
- funcgen = buf_lookup_full_code
- else:
- if mode == 'strided':
- funcname = "__Pyx_BufPtrStrided%dd" % nd
- funcgen = buf_lookup_strided_code
- elif mode == 'c':
- funcname = "__Pyx_BufPtrCContig%dd" % nd
- funcgen = buf_lookup_c_code
- elif mode == 'fortran':
- funcname = "__Pyx_BufPtrFortranContig%dd" % nd
- funcgen = buf_lookup_fortran_code
- else:
- assert False
- for i, s in zip(index_cnames, self.get_buf_stridevars()):
- params.append(i)
- params.append(s)
- # Make sure the utility code is available
- if funcname not in code.globalstate.utility_codes:
- code.globalstate.utility_codes.add(funcname)
- protocode = code.globalstate['utility_code_proto']
- defcode = code.globalstate['utility_code_def']
- funcgen(protocode, defcode, name=funcname, nd=nd)
- buf_ptr_type_code = self.buf_ptr_type.empty_declaration_code()
- ptrcode = "%s(%s, %s, %s)" % (funcname, buf_ptr_type_code, self.buf_ptr,
- ", ".join(params))
- return ptrcode
- def get_flags(buffer_aux, buffer_type):
- flags = 'PyBUF_FORMAT'
- mode = buffer_type.mode
- if mode == 'full':
- flags += '| PyBUF_INDIRECT'
- elif mode == 'strided':
- flags += '| PyBUF_STRIDES'
- elif mode == 'c':
- flags += '| PyBUF_C_CONTIGUOUS'
- elif mode == 'fortran':
- flags += '| PyBUF_F_CONTIGUOUS'
- else:
- assert False
- if buffer_aux.writable_needed: flags += "| PyBUF_WRITABLE"
- return flags
- def used_buffer_aux_vars(entry):
- buffer_aux = entry.buffer_aux
- buffer_aux.buflocal_nd_var.used = True
- buffer_aux.rcbuf_var.used = True
- def put_unpack_buffer_aux_into_scope(buf_entry, code):
- # Generate code to copy the needed struct info into local
- # variables.
- buffer_aux, mode = buf_entry.buffer_aux, buf_entry.type.mode
- pybuffernd_struct = buffer_aux.buflocal_nd_var.cname
- fldnames = ['strides', 'shape']
- if mode == 'full':
- fldnames.append('suboffsets')
- ln = []
- for i in range(buf_entry.type.ndim):
- for fldname in fldnames:
- ln.append("%s.diminfo[%d].%s = %s.rcbuffer->pybuffer.%s[%d];" % \
- (pybuffernd_struct, i, fldname,
- pybuffernd_struct, fldname, i))
- code.putln(' '.join(ln))
- def put_init_vars(entry, code):
- bufaux = entry.buffer_aux
- pybuffernd_struct = bufaux.buflocal_nd_var.cname
- pybuffer_struct = bufaux.rcbuf_var.cname
- # init pybuffer_struct
- code.putln("%s.pybuffer.buf = NULL;" % pybuffer_struct)
- code.putln("%s.refcount = 0;" % pybuffer_struct)
- # init the buffer object
- # code.put_init_var_to_py_none(entry)
- # init the pybuffernd_struct
- code.putln("%s.data = NULL;" % pybuffernd_struct)
- code.putln("%s.rcbuffer = &%s;" % (pybuffernd_struct, pybuffer_struct))
- def put_acquire_arg_buffer(entry, code, pos):
- buffer_aux = entry.buffer_aux
- getbuffer = get_getbuffer_call(code, entry.cname, buffer_aux, entry.type)
- # Acquire any new buffer
- code.putln("{")
- code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % entry.type.dtype.struct_nesting_depth())
- code.putln(code.error_goto_if("%s == -1" % getbuffer, pos))
- code.putln("}")
- # An exception raised in arg parsing cannot be caught, so no
- # need to care about the buffer then.
- put_unpack_buffer_aux_into_scope(entry, code)
- def put_release_buffer_code(code, entry):
- code.globalstate.use_utility_code(acquire_utility_code)
- code.putln("__Pyx_SafeReleaseBuffer(&%s.rcbuffer->pybuffer);" % entry.buffer_aux.buflocal_nd_var.cname)
- def get_getbuffer_call(code, obj_cname, buffer_aux, buffer_type):
- ndim = buffer_type.ndim
- cast = int(buffer_type.cast)
- flags = get_flags(buffer_aux, buffer_type)
- pybuffernd_struct = buffer_aux.buflocal_nd_var.cname
- dtype_typeinfo = get_type_information_cname(code, buffer_type.dtype)
- code.globalstate.use_utility_code(acquire_utility_code)
- return ("__Pyx_GetBufferAndValidate(&%(pybuffernd_struct)s.rcbuffer->pybuffer, "
- "(PyObject*)%(obj_cname)s, &%(dtype_typeinfo)s, %(flags)s, %(ndim)d, "
- "%(cast)d, __pyx_stack)" % locals())
- def put_assign_to_buffer(lhs_cname, rhs_cname, buf_entry,
- is_initialized, pos, code):
- """
- Generate code for reassigning a buffer variables. This only deals with getting
- the buffer auxiliary structure and variables set up correctly, the assignment
- itself and refcounting is the responsibility of the caller.
- However, the assignment operation may throw an exception so that the reassignment
- never happens.
- Depending on the circumstances there are two possible outcomes:
- - Old buffer released, new acquired, rhs assigned to lhs
- - Old buffer released, new acquired which fails, reaqcuire old lhs buffer
- (which may or may not succeed).
- """
- buffer_aux, buffer_type = buf_entry.buffer_aux, buf_entry.type
- pybuffernd_struct = buffer_aux.buflocal_nd_var.cname
- flags = get_flags(buffer_aux, buffer_type)
- code.putln("{") # Set up necessary stack for getbuffer
- code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % buffer_type.dtype.struct_nesting_depth())
- getbuffer = get_getbuffer_call(code, "%s", buffer_aux, buffer_type) # fill in object below
- if is_initialized:
- # Release any existing buffer
- code.putln('__Pyx_SafeReleaseBuffer(&%s.rcbuffer->pybuffer);' % pybuffernd_struct)
- # Acquire
- retcode_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
- code.putln("%s = %s;" % (retcode_cname, getbuffer % rhs_cname))
- code.putln('if (%s) {' % (code.unlikely("%s < 0" % retcode_cname)))
- # If acquisition failed, attempt to reacquire the old buffer
- # before raising the exception. A failure of reacquisition
- # will cause the reacquisition exception to be reported, one
- # can consider working around this later.
- exc_temps = tuple(code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=False)
- for _ in range(3))
- code.putln('PyErr_Fetch(&%s, &%s, &%s);' % exc_temps)
- code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % lhs_cname)))
- code.putln('Py_XDECREF(%s); Py_XDECREF(%s); Py_XDECREF(%s);' % exc_temps) # Do not refnanny these!
- code.globalstate.use_utility_code(raise_buffer_fallback_code)
- code.putln('__Pyx_RaiseBufferFallbackError();')
- code.putln('} else {')
- code.putln('PyErr_Restore(%s, %s, %s);' % exc_temps)
- code.putln('}')
- code.putln('%s = %s = %s = 0;' % exc_temps)
- for t in exc_temps:
- code.funcstate.release_temp(t)
- code.putln('}')
- # Unpack indices
- put_unpack_buffer_aux_into_scope(buf_entry, code)
- code.putln(code.error_goto_if_neg(retcode_cname, pos))
- code.funcstate.release_temp(retcode_cname)
- else:
- # Our entry had no previous value, so set to None when acquisition fails.
- # In this case, auxiliary vars should be set up right in initialization to a zero-buffer,
- # so it suffices to set the buf field to NULL.
- code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % rhs_cname)))
- code.putln('%s = %s; __Pyx_INCREF(Py_None); %s.rcbuffer->pybuffer.buf = NULL;' %
- (lhs_cname,
- PyrexTypes.typecast(buffer_type, PyrexTypes.py_object_type, "Py_None"),
- pybuffernd_struct))
- code.putln(code.error_goto(pos))
- code.put('} else {')
- # Unpack indices
- put_unpack_buffer_aux_into_scope(buf_entry, code)
- code.putln('}')
- code.putln("}") # Release stack
- def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives,
- pos, code, negative_indices, in_nogil_context):
- """
- Generates code to process indices and calculate an offset into
- a buffer. Returns a C string which gives a pointer which can be
- read from or written to at will (it is an expression so caller should
- store it in a temporary if it is used more than once).
- As the bounds checking can have any number of combinations of unsigned
- arguments, smart optimizations etc. we insert it directly in the function
- body. The lookup however is delegated to a inline function that is instantiated
- once per ndim (lookup with suboffsets tend to get quite complicated).
- entry is a BufferEntry
- """
- negative_indices = directives['wraparound'] and negative_indices
- if directives['boundscheck']:
- # Check bounds and fix negative indices.
- # We allocate a temporary which is initialized to -1, meaning OK (!).
- # If an error occurs, the temp is set to the index dimension the
- # error is occurring at.
- failed_dim_temp = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
- code.putln("%s = -1;" % failed_dim_temp)
- for dim, (signed, cname, shape) in enumerate(zip(index_signeds, index_cnames, entry.get_buf_shapevars())):
- if signed != 0:
- # not unsigned, deal with negative index
- code.putln("if (%s < 0) {" % cname)
- if negative_indices:
- code.putln("%s += %s;" % (cname, shape))
- code.putln("if (%s) %s = %d;" % (
- code.unlikely("%s < 0" % cname),
- failed_dim_temp, dim))
- else:
- code.putln("%s = %d;" % (failed_dim_temp, dim))
- code.put("} else ")
- # check bounds in positive direction
- if signed != 0:
- cast = ""
- else:
- cast = "(size_t)"
- code.putln("if (%s) %s = %d;" % (
- code.unlikely("%s >= %s%s" % (cname, cast, shape)),
- failed_dim_temp, dim))
- if in_nogil_context:
- code.globalstate.use_utility_code(raise_indexerror_nogil)
- func = '__Pyx_RaiseBufferIndexErrorNogil'
- else:
- code.globalstate.use_utility_code(raise_indexerror_code)
- func = '__Pyx_RaiseBufferIndexError'
- code.putln("if (%s) {" % code.unlikely("%s != -1" % failed_dim_temp))
- code.putln('%s(%s);' % (func, failed_dim_temp))
- code.putln(code.error_goto(pos))
- code.putln('}')
- code.funcstate.release_temp(failed_dim_temp)
- elif negative_indices:
- # Only fix negative indices.
- for signed, cname, shape in zip(index_signeds, index_cnames, entry.get_buf_shapevars()):
- if signed != 0:
- code.putln("if (%s < 0) %s += %s;" % (cname, cname, shape))
- return entry.generate_buffer_lookup_code(code, index_cnames)
- def use_bufstruct_declare_code(env):
- env.use_utility_code(buffer_struct_declare_code)
- def buf_lookup_full_code(proto, defin, name, nd):
- """
- Generates a buffer lookup function for the right number
- of dimensions. The function gives back a void* at the right location.
- """
- # _i_ndex, _s_tride, sub_o_ffset
- macroargs = ", ".join(["i%d, s%d, o%d" % (i, i, i) for i in range(nd)])
- proto.putln("#define %s(type, buf, %s) (type)(%s_imp(buf, %s))" % (name, macroargs, name, macroargs))
- 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)])
- proto.putln("static CYTHON_INLINE void* %s_imp(void* buf, %s);" % (name, funcargs))
- defin.putln(dedent("""
- static CYTHON_INLINE void* %s_imp(void* buf, %s) {
- char* ptr = (char*)buf;
- """) % (name, funcargs) + "".join([dedent("""\
- ptr += s%d * i%d;
- if (o%d >= 0) ptr = *((char**)ptr) + o%d;
- """) % (i, i, i, i) for i in range(nd)]
- ) + "\nreturn ptr;\n}")
- def buf_lookup_strided_code(proto, defin, name, nd):
- """
- Generates a buffer lookup function for the right number
- of dimensions. The function gives back a void* at the right location.
- """
- # _i_ndex, _s_tride
- args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
- offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd)])
- proto.putln("#define %s(type, buf, %s) (type)((char*)buf + %s)" % (name, args, offset))
- def buf_lookup_c_code(proto, defin, name, nd):
- """
- Similar to strided lookup, but can assume that the last dimension
- doesn't need a multiplication as long as.
- Still we keep the same signature for now.
- """
- if nd == 1:
- proto.putln("#define %s(type, buf, i0, s0) ((type)buf + i0)" % name)
- else:
- args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
- offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd - 1)])
- proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, nd - 1))
- def buf_lookup_fortran_code(proto, defin, name, nd):
- """
- Like C lookup, but the first index is optimized instead.
- """
- if nd == 1:
- proto.putln("#define %s(type, buf, i0, s0) ((type)buf + i0)" % name)
- else:
- args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
- offset = " + ".join(["i%d * s%d" % (i, i) for i in range(1, nd)])
- proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, 0))
- def use_py2_buffer_functions(env):
- env.use_utility_code(GetAndReleaseBufferUtilityCode())
- class GetAndReleaseBufferUtilityCode(object):
- # Emulation of PyObject_GetBuffer and PyBuffer_Release for Python 2.
- # For >= 2.6 we do double mode -- use the new buffer interface on objects
- # which has the right tp_flags set, but emulation otherwise.
- requires = None
- is_cython_utility = False
- def __init__(self):
- pass
- def __eq__(self, other):
- return isinstance(other, GetAndReleaseBufferUtilityCode)
- def __hash__(self):
- return 24342342
- def get_tree(self, **kwargs): pass
- def put_code(self, output):
- code = output['utility_code_def']
- proto_code = output['utility_code_proto']
- env = output.module_node.scope
- cython_scope = env.context.cython_scope
- # Search all types for __getbuffer__ overloads
- types = []
- visited_scopes = set()
- def find_buffer_types(scope):
- if scope in visited_scopes:
- return
- visited_scopes.add(scope)
- for m in scope.cimported_modules:
- find_buffer_types(m)
- for e in scope.type_entries:
- if isinstance(e.utility_code_definition, CythonUtilityCode):
- continue
- t = e.type
- if t.is_extension_type:
- if scope is cython_scope and not e.used:
- continue
- release = get = None
- for x in t.scope.pyfunc_entries:
- if x.name == u"__getbuffer__": get = x.func_cname
- elif x.name == u"__releasebuffer__": release = x.func_cname
- if get:
- types.append((t.typeptr_cname, get, release))
- find_buffer_types(env)
- util_code = TempitaUtilityCode.load(
- "GetAndReleaseBuffer", from_file="Buffer.c",
- context=dict(types=types))
- proto = util_code.format_code(util_code.proto)
- impl = util_code.format_code(
- util_code.inject_string_constants(util_code.impl, output)[1])
- proto_code.putln(proto)
- code.putln(impl)
- def mangle_dtype_name(dtype):
- # Use prefixes to separate user defined types from builtins
- # (consider "typedef float unsigned_int")
- if dtype.is_pyobject:
- return "object"
- elif dtype.is_ptr:
- return "ptr"
- else:
- if dtype.is_typedef or dtype.is_struct_or_union:
- prefix = "nn_"
- else:
- prefix = ""
- return prefix + dtype.specialization_name()
- def get_type_information_cname(code, dtype, maxdepth=None):
- """
- Output the run-time type information (__Pyx_TypeInfo) for given dtype,
- and return the name of the type info struct.
- Structs with two floats of the same size are encoded as complex numbers.
- One can separate between complex numbers declared as struct or with native
- encoding by inspecting to see if the fields field of the type is
- filled in.
- """
- namesuffix = mangle_dtype_name(dtype)
- name = "__Pyx_TypeInfo_%s" % namesuffix
- structinfo_name = "__Pyx_StructFields_%s" % namesuffix
- if dtype.is_error: return "<error>"
- # It's critical that walking the type info doesn't use more stack
- # depth than dtype.struct_nesting_depth() returns, so use an assertion for this
- if maxdepth is None: maxdepth = dtype.struct_nesting_depth()
- if maxdepth <= 0:
- assert False
- if name not in code.globalstate.utility_codes:
- code.globalstate.utility_codes.add(name)
- typecode = code.globalstate['typeinfo']
- arraysizes = []
- if dtype.is_array:
- while dtype.is_array:
- arraysizes.append(dtype.size)
- dtype = dtype.base_type
- complex_possible = dtype.is_struct_or_union and dtype.can_be_complex()
- declcode = dtype.empty_declaration_code()
- if dtype.is_simple_buffer_dtype():
- structinfo_name = "NULL"
- elif dtype.is_struct:
- struct_scope = dtype.scope
- if dtype.is_const:
- struct_scope = struct_scope.const_base_type_scope
- # Must pre-call all used types in order not to recurse during utility code writing.
- fields = struct_scope.var_entries
- assert len(fields) > 0
- types = [get_type_information_cname(code, f.type, maxdepth - 1)
- for f in fields]
- typecode.putln("static __Pyx_StructField %s[] = {" % structinfo_name, safe=True)
- for f, typeinfo in zip(fields, types):
- typecode.putln(' {&%s, "%s", offsetof(%s, %s)},' %
- (typeinfo, f.name, dtype.empty_declaration_code(), f.cname), safe=True)
- typecode.putln(' {NULL, NULL, 0}', safe=True)
- typecode.putln("};", safe=True)
- else:
- assert False
- rep = str(dtype)
- flags = "0"
- is_unsigned = "0"
- if dtype is PyrexTypes.c_char_type:
- is_unsigned = "IS_UNSIGNED(%s)" % declcode
- typegroup = "'H'"
- elif dtype.is_int:
- is_unsigned = "IS_UNSIGNED(%s)" % declcode
- typegroup = "%s ? 'U' : 'I'" % is_unsigned
- elif complex_possible or dtype.is_complex:
- typegroup = "'C'"
- elif dtype.is_float:
- typegroup = "'R'"
- elif dtype.is_struct:
- typegroup = "'S'"
- if dtype.packed:
- flags = "__PYX_BUF_FLAGS_PACKED_STRUCT"
- elif dtype.is_pyobject:
- typegroup = "'O'"
- else:
- assert False, dtype
- typeinfo = ('static __Pyx_TypeInfo %s = '
- '{ "%s", %s, sizeof(%s), { %s }, %s, %s, %s, %s };')
- tup = (name, rep, structinfo_name, declcode,
- ', '.join([str(x) for x in arraysizes]) or '0', len(arraysizes),
- typegroup, is_unsigned, flags)
- typecode.putln(typeinfo % tup, safe=True)
- return name
- def load_buffer_utility(util_code_name, context=None, **kwargs):
- if context is None:
- return UtilityCode.load(util_code_name, "Buffer.c", **kwargs)
- else:
- return TempitaUtilityCode.load(util_code_name, "Buffer.c", context=context, **kwargs)
- context = dict(max_dims=Options.buffer_max_dims)
- buffer_struct_declare_code = load_buffer_utility("BufferStructDeclare", context=context)
- buffer_formats_declare_code = load_buffer_utility("BufferFormatStructs")
- # Utility function to set the right exception
- # The caller should immediately goto_error
- raise_indexerror_code = load_buffer_utility("BufferIndexError")
- raise_indexerror_nogil = load_buffer_utility("BufferIndexErrorNogil")
- raise_buffer_fallback_code = load_buffer_utility("BufferFallbackError")
- acquire_utility_code = load_buffer_utility("BufferGetAndValidate", context=context)
- buffer_format_check_code = load_buffer_utility("BufferFormatCheck", context=context)
- # See utility code BufferFormatFromTypeInfo
- _typeinfo_to_format_code = load_buffer_utility("TypeInfoToFormat")
|