Index: third_party/cython/src/Cython/Compiler/Buffer.py |
diff --git a/third_party/cython/src/Cython/Compiler/Buffer.py b/third_party/cython/src/Cython/Compiler/Buffer.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..229ab8b3853494f78f9547359c4df19253e80b30 |
--- /dev/null |
+++ b/third_party/cython/src/Cython/Compiler/Buffer.py |
@@ -0,0 +1,744 @@ |
+from Cython.Compiler.Visitor import CythonTransform |
+from Cython.Compiler.ModuleNode import ModuleNode |
+from Cython.Compiler.Errors import CompileError |
+from Cython.Compiler.UtilityCode import CythonUtilityCode |
+from Cython.Compiler.Code import UtilityCode, TempitaUtilityCode |
+ |
+from Cython.Compiler import Options |
+from Cython.Compiler import Interpreter |
+from Cython.Compiler import PyrexTypes |
+from Cython.Compiler import Naming |
+from Cython.Compiler 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) |
+ node.scope.use_utility_code(empty_bufstruct_utility) |
+ |
+ 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 |
+ bufvars = [entry for name, entry |
+ in scope.entries.iteritems() |
+ 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.entries.iteritems() |
+ if entry.type.is_memoryviewslice] |
+ if len(memviewslicevars) > 0: |
+ self.buffers_exists = True |
+ |
+ |
+ for (name, entry) in scope.entries.iteritems(): |
+ if name == 'memoryview' and isinstance(entry.utility_code_definition, CythonUtilityCode): |
+ self.using_memoryview = True |
+ break |
+ |
+ |
+ 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.iteritems(): |
+ 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 = self.entry.type.buffer_ptr_type |
+ |
+ 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.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): |
+ code.globalstate.use_utility_code(acquire_utility_code) |
+ 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 catched, 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) |
+ |
+ 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 |
+ code.globalstate.use_utility_code(acquire_utility_code) |
+ pybuffernd_struct = buffer_aux.buflocal_nd_var.cname |
+ flags = get_flags(buffer_aux, buffer_type) |
+ |
+ code.putln("{") # Set up necesarry 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. |
+ type, value, tb = [code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=False) |
+ for i in range(3)] |
+ code.putln('PyErr_Fetch(&%s, &%s, &%s);' % (type, value, tb)) |
+ code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % lhs_cname))) |
+ code.putln('Py_XDECREF(%s); Py_XDECREF(%s); Py_XDECREF(%s);' % (type, value, tb)) # 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);' % (type, value, tb)) |
+ for t in (type, value, tb): |
+ code.funcstate.release_temp(t) |
+ code.putln('}') |
+ 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 dimension index the |
+ # error is occuring at. |
+ tmp_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False) |
+ code.putln("%s = -1;" % tmp_cname) |
+ 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), tmp_cname, dim)) |
+ else: |
+ code.putln("%s = %d;" % (tmp_cname, 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)), |
+ tmp_cname, 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" % tmp_cname)) |
+ code.putln('%s(%s);' % (func, tmp_cname)) |
+ code.putln(code.error_goto(pos)) |
+ code.putln('}') |
+ code.funcstate.release_temp(tmp_cname) |
+ 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 get_empty_bufstruct_code(max_ndim): |
+ code = dedent(""" |
+ static Py_ssize_t __Pyx_zeros[] = {%s}; |
+ static Py_ssize_t __Pyx_minusones[] = {%s}; |
+ """) % (", ".join(["0"] * max_ndim), ", ".join(["-1"] * max_ndim)) |
+ return UtilityCode(proto=code) |
+ |
+empty_bufstruct_utility = get_empty_bufstruct_code(Options.buffer_max_dims) |
+ |
+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): 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 seperate 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 = "" |
+ type_decl = dtype.declaration_code("") |
+ type_decl = type_decl.replace(" ", "_") |
+ return prefix + type_decl.replace("[", "_").replace("]", "_") |
+ |
+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 seperate 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.declaration_code("") |
+ if dtype.is_simple_buffer_dtype(): |
+ structinfo_name = "NULL" |
+ elif dtype.is_struct: |
+ fields = dtype.scope.var_entries |
+ # Must pre-call all used types in order not to recurse utility code |
+ # writing. |
+ 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.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=str(Options.buffer_max_dims)) |
+buffer_struct_declare_code = load_buffer_utility("BufferStructDeclare", |
+ context=context) |
+ |
+ |
+# 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") |
+buffer_structs_code = load_buffer_utility( |
+ "BufferFormatStructs", proto_block='utility_code_proto_before_types') |
+acquire_utility_code = load_buffer_utility("BufferFormatCheck", |
+ context=context, |
+ requires=[buffer_structs_code]) |
+ |
+# See utility code BufferFormatFromTypeInfo |
+_typeinfo_to_format_code = load_buffer_utility("TypeInfoToFormat", context={}, |
+ requires=[buffer_structs_code]) |
+typeinfo_compare_code = load_buffer_utility("TypeInfoCompare", context={}, |
+ requires=[buffer_structs_code]) |