Index: third_party/cython/src/Cython/Compiler/MemoryView.py |
diff --git a/third_party/cython/src/Cython/Compiler/MemoryView.py b/third_party/cython/src/Cython/Compiler/MemoryView.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9adb194d311392b468d5bcff74b6108f12cc0b12 |
--- /dev/null |
+++ b/third_party/cython/src/Cython/Compiler/MemoryView.py |
@@ -0,0 +1,955 @@ |
+from Errors import CompileError, error |
+import ExprNodes |
+from ExprNodes import IntNode, NameNode, AttributeNode |
+import Options |
+from Code import UtilityCode, TempitaUtilityCode |
+from UtilityCode import CythonUtilityCode |
+import Buffer |
+import PyrexTypes |
+import ModuleNode |
+ |
+START_ERR = "Start must not be given." |
+STOP_ERR = "Axis specification only allowed in the 'step' slot." |
+STEP_ERR = "Step must be omitted, 1, or a valid specifier." |
+BOTH_CF_ERR = "Cannot specify an array that is both C and Fortran contiguous." |
+INVALID_ERR = "Invalid axis specification." |
+NOT_CIMPORTED_ERR = "Variable was not cimported from cython.view" |
+EXPR_ERR = "no expressions allowed in axis spec, only names and literals." |
+CF_ERR = "Invalid axis specification for a C/Fortran contiguous array." |
+ERR_UNINITIALIZED = ("Cannot check if memoryview %s is initialized without the " |
+ "GIL, consider using initializedcheck(False)") |
+ |
+def err_if_nogil_initialized_check(pos, env, name='variable'): |
+ "This raises an exception at runtime now" |
+ pass |
+ #if env.nogil and env.directives['initializedcheck']: |
+ #error(pos, ERR_UNINITIALIZED % name) |
+ |
+def concat_flags(*flags): |
+ return "(%s)" % "|".join(flags) |
+ |
+format_flag = "PyBUF_FORMAT" |
+ |
+memview_c_contiguous = "(PyBUF_C_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)" |
+memview_f_contiguous = "(PyBUF_F_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)" |
+memview_any_contiguous = "(PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)" |
+memview_full_access = "PyBUF_FULL" |
+#memview_strided_access = "PyBUF_STRIDED" |
+memview_strided_access = "PyBUF_RECORDS" |
+ |
+MEMVIEW_DIRECT = '__Pyx_MEMVIEW_DIRECT' |
+MEMVIEW_PTR = '__Pyx_MEMVIEW_PTR' |
+MEMVIEW_FULL = '__Pyx_MEMVIEW_FULL' |
+MEMVIEW_CONTIG = '__Pyx_MEMVIEW_CONTIG' |
+MEMVIEW_STRIDED= '__Pyx_MEMVIEW_STRIDED' |
+MEMVIEW_FOLLOW = '__Pyx_MEMVIEW_FOLLOW' |
+ |
+_spec_to_const = { |
+ 'direct' : MEMVIEW_DIRECT, |
+ 'ptr' : MEMVIEW_PTR, |
+ 'full' : MEMVIEW_FULL, |
+ 'contig' : MEMVIEW_CONTIG, |
+ 'strided': MEMVIEW_STRIDED, |
+ 'follow' : MEMVIEW_FOLLOW, |
+ } |
+ |
+_spec_to_abbrev = { |
+ 'direct' : 'd', |
+ 'ptr' : 'p', |
+ 'full' : 'f', |
+ 'contig' : 'c', |
+ 'strided' : 's', |
+ 'follow' : '_', |
+} |
+ |
+memslice_entry_init = "{ 0, 0, { 0 }, { 0 }, { 0 } }" |
+ |
+memview_name = u'memoryview' |
+memview_typeptr_cname = '__pyx_memoryview_type' |
+memview_objstruct_cname = '__pyx_memoryview_obj' |
+memviewslice_cname = u'__Pyx_memviewslice' |
+ |
+def put_init_entry(mv_cname, code): |
+ code.putln("%s.data = NULL;" % mv_cname) |
+ code.putln("%s.memview = NULL;" % mv_cname) |
+ |
+def mangle_dtype_name(dtype): |
+ # a dumb wrapper for now; move Buffer.mangle_dtype_name in here later? |
+ import Buffer |
+ return Buffer.mangle_dtype_name(dtype) |
+ |
+#def axes_to_str(axes): |
+# return "".join([access[0].upper()+packing[0] for (access, packing) in axes]) |
+ |
+def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code, |
+ have_gil=False, first_assignment=True): |
+ "We can avoid decreffing the lhs if we know it is the first assignment" |
+ assert rhs.type.is_memoryviewslice |
+ |
+ pretty_rhs = rhs.result_in_temp() or rhs.is_simple() |
+ if pretty_rhs: |
+ rhstmp = rhs.result() |
+ else: |
+ rhstmp = code.funcstate.allocate_temp(lhs_type, manage_ref=False) |
+ code.putln("%s = %s;" % (rhstmp, rhs.result_as(lhs_type))) |
+ |
+ # Allow uninitialized assignment |
+ #code.putln(code.put_error_if_unbound(lhs_pos, rhs.entry)) |
+ put_assign_to_memviewslice(lhs_cname, rhs, rhstmp, lhs_type, code, |
+ have_gil=have_gil, first_assignment=first_assignment) |
+ |
+ if not pretty_rhs: |
+ code.funcstate.release_temp(rhstmp) |
+ |
+def put_assign_to_memviewslice(lhs_cname, rhs, rhs_cname, memviewslicetype, code, |
+ have_gil=False, first_assignment=False): |
+ if not first_assignment: |
+ code.put_xdecref_memoryviewslice(lhs_cname, have_gil=have_gil) |
+ |
+ if not rhs.result_in_temp(): |
+ rhs.make_owned_memoryviewslice(code) |
+ |
+ code.putln("%s = %s;" % (lhs_cname, rhs_cname)) |
+ |
+def get_buf_flags(specs): |
+ is_c_contig, is_f_contig = is_cf_contig(specs) |
+ |
+ if is_c_contig: |
+ return memview_c_contiguous |
+ elif is_f_contig: |
+ return memview_f_contiguous |
+ |
+ access, packing = zip(*specs) |
+ |
+ if 'full' in access or 'ptr' in access: |
+ return memview_full_access |
+ else: |
+ return memview_strided_access |
+ |
+def insert_newaxes(memoryviewtype, n): |
+ axes = [('direct', 'strided')] * n |
+ axes.extend(memoryviewtype.axes) |
+ return PyrexTypes.MemoryViewSliceType(memoryviewtype.dtype, axes) |
+ |
+def broadcast_types(src, dst): |
+ n = abs(src.ndim - dst.ndim) |
+ if src.ndim < dst.ndim: |
+ return insert_newaxes(src, n), dst |
+ else: |
+ return src, insert_newaxes(dst, n) |
+ |
+def src_conforms_to_dst(src, dst, broadcast=False): |
+ ''' |
+ returns True if src conforms to dst, False otherwise. |
+ |
+ If conformable, the types are the same, the ndims are equal, and each axis spec is conformable. |
+ |
+ Any packing/access spec is conformable to itself. |
+ |
+ 'direct' and 'ptr' are conformable to 'full'. |
+ 'contig' and 'follow' are conformable to 'strided'. |
+ Any other combo is not conformable. |
+ ''' |
+ |
+ if src.dtype != dst.dtype: |
+ return False |
+ |
+ if src.ndim != dst.ndim: |
+ if broadcast: |
+ src, dst = broadcast_types(src, dst) |
+ else: |
+ return False |
+ |
+ for src_spec, dst_spec in zip(src.axes, dst.axes): |
+ src_access, src_packing = src_spec |
+ dst_access, dst_packing = dst_spec |
+ if src_access != dst_access and dst_access != 'full': |
+ return False |
+ if src_packing != dst_packing and dst_packing != 'strided': |
+ return False |
+ |
+ return True |
+ |
+def valid_memslice_dtype(dtype, i=0): |
+ """ |
+ Return whether type dtype can be used as the base type of a |
+ memoryview slice. |
+ |
+ We support structs, numeric types and objects |
+ """ |
+ if dtype.is_complex and dtype.real_type.is_int: |
+ return False |
+ |
+ if dtype is PyrexTypes.c_bint_type: |
+ return False |
+ |
+ if dtype.is_struct and dtype.kind == 'struct': |
+ for member in dtype.scope.var_entries: |
+ if not valid_memslice_dtype(member.type): |
+ return False |
+ |
+ return True |
+ |
+ return ( |
+ dtype.is_error or |
+ # Pointers are not valid (yet) |
+ # (dtype.is_ptr and valid_memslice_dtype(dtype.base_type)) or |
+ (dtype.is_array and i < 8 and |
+ valid_memslice_dtype(dtype.base_type, i + 1)) or |
+ dtype.is_numeric or |
+ dtype.is_pyobject or |
+ dtype.is_fused or # accept this as it will be replaced by specializations later |
+ (dtype.is_typedef and valid_memslice_dtype(dtype.typedef_base_type)) |
+ ) |
+ |
+def validate_memslice_dtype(pos, dtype): |
+ if not valid_memslice_dtype(dtype): |
+ error(pos, "Invalid base type for memoryview slice: %s" % dtype) |
+ |
+ |
+class MemoryViewSliceBufferEntry(Buffer.BufferEntry): |
+ def __init__(self, entry): |
+ self.entry = entry |
+ self.type = entry.type |
+ self.cname = entry.cname |
+ self.buf_ptr = "%s.data" % self.cname |
+ |
+ dtype = self.entry.type.dtype |
+ dtype = PyrexTypes.CPtrType(dtype) |
+ |
+ self.buf_ptr_type = dtype |
+ |
+ def get_buf_suboffsetvars(self): |
+ return self._for_all_ndim("%s.suboffsets[%d]") |
+ |
+ def get_buf_stridevars(self): |
+ return self._for_all_ndim("%s.strides[%d]") |
+ |
+ def get_buf_shapevars(self): |
+ return self._for_all_ndim("%s.shape[%d]") |
+ |
+ def generate_buffer_lookup_code(self, code, index_cnames): |
+ axes = [(dim, index_cnames[dim], access, packing) |
+ for dim, (access, packing) in enumerate(self.type.axes)] |
+ return self._generate_buffer_lookup_code(code, axes) |
+ |
+ def _generate_buffer_lookup_code(self, code, axes, cast_result=True): |
+ bufp = self.buf_ptr |
+ type_decl = self.type.dtype.declaration_code("") |
+ |
+ for dim, index, access, packing in axes: |
+ shape = "%s.shape[%d]" % (self.cname, dim) |
+ stride = "%s.strides[%d]" % (self.cname, dim) |
+ suboffset = "%s.suboffsets[%d]" % (self.cname, dim) |
+ |
+ flag = get_memoryview_flag(access, packing) |
+ |
+ if flag in ("generic", "generic_contiguous"): |
+ # Note: we cannot do cast tricks to avoid stride multiplication |
+ # for generic_contiguous, as we may have to do (dtype *) |
+ # or (dtype **) arithmetic, we won't know which unless |
+ # we check suboffsets |
+ code.globalstate.use_utility_code(memviewslice_index_helpers) |
+ bufp = ('__pyx_memviewslice_index_full(%s, %s, %s, %s)' % |
+ (bufp, index, stride, suboffset)) |
+ |
+ elif flag == "indirect": |
+ bufp = "(%s + %s * %s)" % (bufp, index, stride) |
+ bufp = ("(*((char **) %s) + %s)" % (bufp, suboffset)) |
+ |
+ elif flag == "indirect_contiguous": |
+ # Note: we do char ** arithmetic |
+ bufp = "(*((char **) %s + %s) + %s)" % (bufp, index, suboffset) |
+ |
+ elif flag == "strided": |
+ bufp = "(%s + %s * %s)" % (bufp, index, stride) |
+ |
+ else: |
+ assert flag == 'contiguous', flag |
+ bufp = '((char *) (((%s *) %s) + %s))' % (type_decl, bufp, index) |
+ |
+ bufp = '( /* dim=%d */ %s )' % (dim, bufp) |
+ |
+ if cast_result: |
+ return "((%s *) %s)" % (type_decl, bufp) |
+ |
+ return bufp |
+ |
+ def generate_buffer_slice_code(self, code, indices, dst, have_gil, |
+ have_slices, directives): |
+ """ |
+ Slice a memoryviewslice. |
+ |
+ indices - list of index nodes. If not a SliceNode, or NoneNode, |
+ then it must be coercible to Py_ssize_t |
+ |
+ Simply call __pyx_memoryview_slice_memviewslice with the right |
+ arguments. |
+ """ |
+ new_ndim = 0 |
+ src = self.cname |
+ |
+ def load_slice_util(name, dict): |
+ proto, impl = TempitaUtilityCode.load_as_string( |
+ name, "MemoryView_C.c", context=dict) |
+ return impl |
+ |
+ all_dimensions_direct = True |
+ for access, packing in self.type.axes: |
+ if access != 'direct': |
+ all_dimensions_direct = False |
+ break |
+ |
+ no_suboffset_dim = all_dimensions_direct and not have_slices |
+ if not no_suboffset_dim: |
+ suboffset_dim = code.funcstate.allocate_temp( |
+ PyrexTypes.c_int_type, False) |
+ code.putln("%s = -1;" % suboffset_dim) |
+ |
+ code.putln("%(dst)s.data = %(src)s.data;" % locals()) |
+ code.putln("%(dst)s.memview = %(src)s.memview;" % locals()) |
+ code.put_incref_memoryviewslice(dst) |
+ |
+ dim = -1 |
+ for index in indices: |
+ error_goto = code.error_goto(index.pos) |
+ if not index.is_none: |
+ dim += 1 |
+ access, packing = self.type.axes[dim] |
+ |
+ if isinstance(index, ExprNodes.SliceNode): |
+ # slice, unspecified dimension, or part of ellipsis |
+ d = locals() |
+ for s in "start stop step".split(): |
+ idx = getattr(index, s) |
+ have_idx = d['have_' + s] = not idx.is_none |
+ if have_idx: |
+ d[s] = idx.result() |
+ else: |
+ d[s] = "0" |
+ |
+ if (not d['have_start'] and |
+ not d['have_stop'] and |
+ not d['have_step']): |
+ # full slice (:), simply copy over the extent, stride |
+ # and suboffset. Also update suboffset_dim if needed |
+ d['access'] = access |
+ code.put(load_slice_util("SimpleSlice", d)) |
+ else: |
+ code.put(load_slice_util("ToughSlice", d)) |
+ |
+ new_ndim += 1 |
+ |
+ elif index.is_none: |
+ # newaxis |
+ attribs = [('shape', 1), ('strides', 0), ('suboffsets', -1)] |
+ for attrib, value in attribs: |
+ code.putln("%s.%s[%d] = %d;" % (dst, attrib, new_ndim, value)) |
+ |
+ new_ndim += 1 |
+ |
+ else: |
+ # normal index |
+ idx = index.result() |
+ |
+ if access == 'direct': |
+ indirect = False |
+ else: |
+ indirect = True |
+ generic = (access == 'full') |
+ if new_ndim != 0: |
+ return error(index.pos, |
+ "All preceding dimensions must be " |
+ "indexed and not sliced") |
+ |
+ wraparound = int(directives['wraparound']) |
+ boundscheck = int(directives['boundscheck']) |
+ d = locals() |
+ code.put(load_slice_util("SliceIndex", d)) |
+ |
+ if not no_suboffset_dim: |
+ code.funcstate.release_temp(suboffset_dim) |
+ |
+ |
+def empty_slice(pos): |
+ none = ExprNodes.NoneNode(pos) |
+ return ExprNodes.SliceNode(pos, start=none, |
+ stop=none, step=none) |
+ |
+def unellipsify(indices, newaxes, ndim): |
+ result = [] |
+ seen_ellipsis = False |
+ have_slices = False |
+ |
+ n_indices = len(indices) - len(newaxes) |
+ |
+ for index in indices: |
+ if isinstance(index, ExprNodes.EllipsisNode): |
+ have_slices = True |
+ full_slice = empty_slice(index.pos) |
+ |
+ if seen_ellipsis: |
+ result.append(full_slice) |
+ else: |
+ nslices = ndim - n_indices + 1 |
+ result.extend([full_slice] * nslices) |
+ seen_ellipsis = True |
+ else: |
+ have_slices = (have_slices or |
+ isinstance(index, ExprNodes.SliceNode) or |
+ index.is_none) |
+ result.append(index) |
+ |
+ result_length = len(result) - len(newaxes) |
+ if result_length < ndim: |
+ have_slices = True |
+ nslices = ndim - result_length |
+ result.extend([empty_slice(indices[-1].pos)] * nslices) |
+ |
+ return have_slices, result |
+ |
+def get_memoryview_flag(access, packing): |
+ if access == 'full' and packing in ('strided', 'follow'): |
+ return 'generic' |
+ elif access == 'full' and packing == 'contig': |
+ return 'generic_contiguous' |
+ elif access == 'ptr' and packing in ('strided', 'follow'): |
+ return 'indirect' |
+ elif access == 'ptr' and packing == 'contig': |
+ return 'indirect_contiguous' |
+ elif access == 'direct' and packing in ('strided', 'follow'): |
+ return 'strided' |
+ else: |
+ assert (access, packing) == ('direct', 'contig'), (access, packing) |
+ return 'contiguous' |
+ |
+def get_is_contig_func_name(c_or_f, ndim): |
+ return "__pyx_memviewslice_is_%s_contig%d" % (c_or_f, ndim) |
+ |
+def get_is_contig_utility(c_contig, ndim): |
+ C = dict(context, ndim=ndim) |
+ if c_contig: |
+ utility = load_memview_c_utility("MemviewSliceIsCContig", C, |
+ requires=[is_contig_utility]) |
+ else: |
+ utility = load_memview_c_utility("MemviewSliceIsFContig", C, |
+ requires=[is_contig_utility]) |
+ |
+ return utility |
+ |
+def copy_src_to_dst_cname(): |
+ return "__pyx_memoryview_copy_contents" |
+ |
+def verify_direct_dimensions(node): |
+ for access, packing in node.type.axes: |
+ if access != 'direct': |
+ error(self.pos, "All dimensions must be direct") |
+ |
+def copy_broadcast_memview_src_to_dst(src, dst, code): |
+ """ |
+ Copy the contents of slice src to slice dst. Does not support indirect |
+ slices. |
+ """ |
+ verify_direct_dimensions(src) |
+ verify_direct_dimensions(dst) |
+ |
+ code.putln(code.error_goto_if_neg( |
+ "%s(%s, %s, %d, %d, %d)" % (copy_src_to_dst_cname(), |
+ src.result(), dst.result(), |
+ src.type.ndim, dst.type.ndim, |
+ dst.type.dtype.is_pyobject), |
+ dst.pos)) |
+ |
+def get_1d_fill_scalar_func(type, code): |
+ dtype = type.dtype |
+ type_decl = dtype.declaration_code("") |
+ |
+ dtype_name = mangle_dtype_name(dtype) |
+ context = dict(dtype_name=dtype_name, type_decl=type_decl) |
+ utility = load_memview_c_utility("FillStrided1DScalar", context) |
+ code.globalstate.use_utility_code(utility) |
+ return '__pyx_fill_slice_%s' % dtype_name |
+ |
+def assign_scalar(dst, scalar, code): |
+ """ |
+ Assign a scalar to a slice. dst must be a temp, scalar will be assigned |
+ to a correct type and not just something assignable. |
+ """ |
+ verify_direct_dimensions(dst) |
+ dtype = dst.type.dtype |
+ type_decl = dtype.declaration_code("") |
+ slice_decl = dst.type.declaration_code("") |
+ |
+ code.begin_block() |
+ code.putln("%s __pyx_temp_scalar = %s;" % (type_decl, scalar.result())) |
+ if dst.result_in_temp() or (dst.base.is_name and |
+ isinstance(dst.index, ExprNodes.EllipsisNode)): |
+ dst_temp = dst.result() |
+ else: |
+ code.putln("%s __pyx_temp_slice = %s;" % (slice_decl, dst.result())) |
+ dst_temp = "__pyx_temp_slice" |
+ |
+ # with slice_iter(dst.type, dst_temp, dst.type.ndim, code) as p: |
+ slice_iter_obj = slice_iter(dst.type, dst_temp, dst.type.ndim, code) |
+ p = slice_iter_obj.start_loops() |
+ |
+ if dtype.is_pyobject: |
+ code.putln("Py_DECREF(*(PyObject **) %s);" % p) |
+ |
+ code.putln("*((%s *) %s) = __pyx_temp_scalar;" % (type_decl, p)) |
+ |
+ if dtype.is_pyobject: |
+ code.putln("Py_INCREF(__pyx_temp_scalar);") |
+ |
+ slice_iter_obj.end_loops() |
+ code.end_block() |
+ |
+def slice_iter(slice_type, slice_temp, ndim, code): |
+ if slice_type.is_c_contig or slice_type.is_f_contig: |
+ return ContigSliceIter(slice_type, slice_temp, ndim, code) |
+ else: |
+ return StridedSliceIter(slice_type, slice_temp, ndim, code) |
+ |
+class SliceIter(object): |
+ def __init__(self, slice_type, slice_temp, ndim, code): |
+ self.slice_type = slice_type |
+ self.slice_temp = slice_temp |
+ self.code = code |
+ self.ndim = ndim |
+ |
+class ContigSliceIter(SliceIter): |
+ def start_loops(self): |
+ code = self.code |
+ code.begin_block() |
+ |
+ type_decl = self.slice_type.dtype.declaration_code("") |
+ |
+ total_size = ' * '.join("%s.shape[%d]" % (self.slice_temp, i) |
+ for i in range(self.ndim)) |
+ code.putln("Py_ssize_t __pyx_temp_extent = %s;" % total_size) |
+ code.putln("Py_ssize_t __pyx_temp_idx;") |
+ code.putln("%s *__pyx_temp_pointer = (%s *) %s.data;" % ( |
+ type_decl, type_decl, self.slice_temp)) |
+ code.putln("for (__pyx_temp_idx = 0; " |
+ "__pyx_temp_idx < __pyx_temp_extent; " |
+ "__pyx_temp_idx++) {") |
+ |
+ return "__pyx_temp_pointer" |
+ |
+ def end_loops(self): |
+ self.code.putln("__pyx_temp_pointer += 1;") |
+ self.code.putln("}") |
+ self.code.end_block() |
+ |
+class StridedSliceIter(SliceIter): |
+ def start_loops(self): |
+ code = self.code |
+ code.begin_block() |
+ |
+ for i in range(self.ndim): |
+ t = i, self.slice_temp, i |
+ code.putln("Py_ssize_t __pyx_temp_extent_%d = %s.shape[%d];" % t) |
+ code.putln("Py_ssize_t __pyx_temp_stride_%d = %s.strides[%d];" % t) |
+ code.putln("char *__pyx_temp_pointer_%d;" % i) |
+ code.putln("Py_ssize_t __pyx_temp_idx_%d;" % i) |
+ |
+ code.putln("__pyx_temp_pointer_0 = %s.data;" % self.slice_temp) |
+ |
+ for i in range(self.ndim): |
+ if i > 0: |
+ code.putln("__pyx_temp_pointer_%d = __pyx_temp_pointer_%d;" % (i, i - 1)) |
+ |
+ code.putln("for (__pyx_temp_idx_%d = 0; " |
+ "__pyx_temp_idx_%d < __pyx_temp_extent_%d; " |
+ "__pyx_temp_idx_%d++) {" % (i, i, i, i)) |
+ |
+ return "__pyx_temp_pointer_%d" % (self.ndim - 1) |
+ |
+ def end_loops(self): |
+ code = self.code |
+ for i in range(self.ndim - 1, -1, -1): |
+ code.putln("__pyx_temp_pointer_%d += __pyx_temp_stride_%d;" % (i, i)) |
+ code.putln("}") |
+ |
+ code.end_block() |
+ |
+ |
+def copy_c_or_fortran_cname(memview): |
+ if memview.is_c_contig: |
+ c_or_f = 'c' |
+ else: |
+ c_or_f = 'f' |
+ |
+ return "__pyx_memoryview_copy_slice_%s_%s" % ( |
+ memview.specialization_suffix(), c_or_f) |
+ |
+def get_copy_new_utility(pos, from_memview, to_memview): |
+ if from_memview.dtype != to_memview.dtype: |
+ return error(pos, "dtypes must be the same!") |
+ if len(from_memview.axes) != len(to_memview.axes): |
+ return error(pos, "number of dimensions must be same") |
+ if not (to_memview.is_c_contig or to_memview.is_f_contig): |
+ return error(pos, "to_memview must be c or f contiguous.") |
+ |
+ for (access, packing) in from_memview.axes: |
+ if access != 'direct': |
+ return error( |
+ pos, "cannot handle 'full' or 'ptr' access at this time.") |
+ |
+ if to_memview.is_c_contig: |
+ mode = 'c' |
+ contig_flag = memview_c_contiguous |
+ elif to_memview.is_f_contig: |
+ mode = 'fortran' |
+ contig_flag = memview_f_contiguous |
+ |
+ return load_memview_c_utility( |
+ "CopyContentsUtility", |
+ context=dict( |
+ context, |
+ mode=mode, |
+ dtype_decl=to_memview.dtype.declaration_code(''), |
+ contig_flag=contig_flag, |
+ ndim=to_memview.ndim, |
+ func_cname=copy_c_or_fortran_cname(to_memview), |
+ dtype_is_object=int(to_memview.dtype.is_pyobject)), |
+ requires=[copy_contents_new_utility]) |
+ |
+def get_axes_specs(env, axes): |
+ ''' |
+ get_axes_specs(env, axes) -> list of (access, packing) specs for each axis. |
+ access is one of 'full', 'ptr' or 'direct' |
+ packing is one of 'contig', 'strided' or 'follow' |
+ ''' |
+ |
+ cythonscope = env.global_scope().context.cython_scope |
+ cythonscope.load_cythonscope() |
+ viewscope = cythonscope.viewscope |
+ |
+ access_specs = tuple([viewscope.lookup(name) |
+ for name in ('full', 'direct', 'ptr')]) |
+ packing_specs = tuple([viewscope.lookup(name) |
+ for name in ('contig', 'strided', 'follow')]) |
+ |
+ is_f_contig, is_c_contig = False, False |
+ default_access, default_packing = 'direct', 'strided' |
+ cf_access, cf_packing = default_access, 'follow' |
+ |
+ axes_specs = [] |
+ # analyse all axes. |
+ for idx, axis in enumerate(axes): |
+ if not axis.start.is_none: |
+ raise CompileError(axis.start.pos, START_ERR) |
+ |
+ if not axis.stop.is_none: |
+ raise CompileError(axis.stop.pos, STOP_ERR) |
+ |
+ if axis.step.is_none: |
+ axes_specs.append((default_access, default_packing)) |
+ |
+ elif isinstance(axis.step, IntNode): |
+ # the packing for the ::1 axis is contiguous, |
+ # all others are cf_packing. |
+ if axis.step.compile_time_value(env) != 1: |
+ raise CompileError(axis.step.pos, STEP_ERR) |
+ |
+ axes_specs.append((cf_access, 'cfcontig')) |
+ |
+ elif isinstance(axis.step, (NameNode, AttributeNode)): |
+ entry = _get_resolved_spec(env, axis.step) |
+ if entry.name in view_constant_to_access_packing: |
+ axes_specs.append(view_constant_to_access_packing[entry.name]) |
+ else: |
+ raise CompilerError(axis.step.pos, INVALID_ERR) |
+ |
+ else: |
+ raise CompileError(axis.step.pos, INVALID_ERR) |
+ |
+ # First, find out if we have a ::1 somewhere |
+ contig_dim = 0 |
+ is_contig = False |
+ for idx, (access, packing) in enumerate(axes_specs): |
+ if packing == 'cfcontig': |
+ if is_contig: |
+ raise CompileError(axis.step.pos, BOTH_CF_ERR) |
+ |
+ contig_dim = idx |
+ axes_specs[idx] = (access, 'contig') |
+ is_contig = True |
+ |
+ if is_contig: |
+ # We have a ::1 somewhere, see if we're C or Fortran contiguous |
+ if contig_dim == len(axes) - 1: |
+ is_c_contig = True |
+ else: |
+ is_f_contig = True |
+ |
+ if contig_dim and not axes_specs[contig_dim - 1][0] in ('full', 'ptr'): |
+ raise CompileError(axes[contig_dim].pos, |
+ "Fortran contiguous specifier must follow an indirect dimension") |
+ |
+ if is_c_contig: |
+ # Contiguous in the last dimension, find the last indirect dimension |
+ contig_dim = -1 |
+ for idx, (access, packing) in enumerate(reversed(axes_specs)): |
+ if access in ('ptr', 'full'): |
+ contig_dim = len(axes) - idx - 1 |
+ |
+ # Replace 'strided' with 'follow' for any dimension following the last |
+ # indirect dimension, the first dimension or the dimension following |
+ # the ::1. |
+ # int[::indirect, ::1, :, :] |
+ # ^ ^ |
+ # int[::indirect, :, :, ::1] |
+ # ^ ^ |
+ start = contig_dim + 1 |
+ stop = len(axes) - is_c_contig |
+ for idx, (access, packing) in enumerate(axes_specs[start:stop]): |
+ idx = contig_dim + 1 + idx |
+ if access != 'direct': |
+ raise CompileError(axes[idx].pos, |
+ "Indirect dimension may not follow " |
+ "Fortran contiguous dimension") |
+ if packing == 'contig': |
+ raise CompileError(axes[idx].pos, |
+ "Dimension may not be contiguous") |
+ axes_specs[idx] = (access, cf_packing) |
+ |
+ if is_c_contig: |
+ # For C contiguity, we need to fix the 'contig' dimension |
+ # after the loop |
+ a, p = axes_specs[-1] |
+ axes_specs[-1] = a, 'contig' |
+ |
+ validate_axes_specs([axis.start.pos for axis in axes], |
+ axes_specs, |
+ is_c_contig, |
+ is_f_contig) |
+ |
+ return axes_specs |
+ |
+def validate_axes(pos, axes): |
+ if len(axes) >= Options.buffer_max_dims: |
+ error(pos, "More dimensions than the maximum number" |
+ " of buffer dimensions were used.") |
+ return False |
+ |
+ return True |
+ |
+def all(it): |
+ for item in it: |
+ if not item: |
+ return False |
+ return True |
+ |
+def is_cf_contig(specs): |
+ is_c_contig = is_f_contig = False |
+ |
+ if (len(specs) == 1 and specs == [('direct', 'contig')]): |
+ is_c_contig = True |
+ |
+ elif (specs[-1] == ('direct','contig') and |
+ all([axis == ('direct','follow') for axis in specs[:-1]])): |
+ # c_contiguous: 'follow', 'follow', ..., 'follow', 'contig' |
+ is_c_contig = True |
+ |
+ elif (len(specs) > 1 and |
+ specs[0] == ('direct','contig') and |
+ all([axis == ('direct','follow') for axis in specs[1:]])): |
+ # f_contiguous: 'contig', 'follow', 'follow', ..., 'follow' |
+ is_f_contig = True |
+ |
+ return is_c_contig, is_f_contig |
+ |
+def get_mode(specs): |
+ is_c_contig, is_f_contig = is_cf_contig(specs) |
+ |
+ if is_c_contig: |
+ return 'c' |
+ elif is_f_contig: |
+ return 'fortran' |
+ |
+ for access, packing in specs: |
+ if access in ('ptr', 'full'): |
+ return 'full' |
+ |
+ return 'strided' |
+ |
+view_constant_to_access_packing = { |
+ 'generic': ('full', 'strided'), |
+ 'strided': ('direct', 'strided'), |
+ 'indirect': ('ptr', 'strided'), |
+ 'generic_contiguous': ('full', 'contig'), |
+ 'contiguous': ('direct', 'contig'), |
+ 'indirect_contiguous': ('ptr', 'contig'), |
+} |
+ |
+def validate_axes_specs(positions, specs, is_c_contig, is_f_contig): |
+ |
+ packing_specs = ('contig', 'strided', 'follow') |
+ access_specs = ('direct', 'ptr', 'full') |
+ |
+ # is_c_contig, is_f_contig = is_cf_contig(specs) |
+ |
+ has_contig = has_follow = has_strided = has_generic_contig = False |
+ |
+ last_indirect_dimension = -1 |
+ for idx, (access, packing) in enumerate(specs): |
+ if access == 'ptr': |
+ last_indirect_dimension = idx |
+ |
+ for idx, pos, (access, packing) in zip(xrange(len(specs)), positions, specs): |
+ |
+ if not (access in access_specs and |
+ packing in packing_specs): |
+ raise CompileError(pos, "Invalid axes specification.") |
+ |
+ if packing == 'strided': |
+ has_strided = True |
+ elif packing == 'contig': |
+ if has_contig: |
+ raise CompileError(pos, "Only one direct contiguous " |
+ "axis may be specified.") |
+ |
+ valid_contig_dims = last_indirect_dimension + 1, len(specs) - 1 |
+ if idx not in valid_contig_dims and access != 'ptr': |
+ if last_indirect_dimension + 1 != len(specs) - 1: |
+ dims = "dimensions %d and %d" % valid_contig_dims |
+ else: |
+ dims = "dimension %d" % valid_contig_dims[0] |
+ |
+ raise CompileError(pos, "Only %s may be contiguous and direct" % dims) |
+ |
+ has_contig = access != 'ptr' |
+ elif packing == 'follow': |
+ if has_strided: |
+ raise CompileError(pos, "A memoryview cannot have both follow and strided axis specifiers.") |
+ if not (is_c_contig or is_f_contig): |
+ raise CompileError(pos, "Invalid use of the follow specifier.") |
+ |
+ if access in ('ptr', 'full'): |
+ has_strided = False |
+ |
+def _get_resolved_spec(env, spec): |
+ # spec must be a NameNode or an AttributeNode |
+ if isinstance(spec, NameNode): |
+ return _resolve_NameNode(env, spec) |
+ elif isinstance(spec, AttributeNode): |
+ return _resolve_AttributeNode(env, spec) |
+ else: |
+ raise CompileError(spec.pos, INVALID_ERR) |
+ |
+def _resolve_NameNode(env, node): |
+ try: |
+ resolved_name = env.lookup(node.name).name |
+ except AttributeError: |
+ raise CompileError(node.pos, INVALID_ERR) |
+ |
+ viewscope = env.global_scope().context.cython_scope.viewscope |
+ entry = viewscope.lookup(resolved_name) |
+ if entry is None: |
+ raise CompileError(node.pos, NOT_CIMPORTED_ERR) |
+ |
+ return entry |
+ |
+def _resolve_AttributeNode(env, node): |
+ path = [] |
+ while isinstance(node, AttributeNode): |
+ path.insert(0, node.attribute) |
+ node = node.obj |
+ if isinstance(node, NameNode): |
+ path.insert(0, node.name) |
+ else: |
+ raise CompileError(node.pos, EXPR_ERR) |
+ modnames = path[:-1] |
+ # must be at least 1 module name, o/w not an AttributeNode. |
+ assert modnames |
+ |
+ scope = env |
+ for modname in modnames: |
+ mod = scope.lookup(modname) |
+ if not mod or not mod.as_module: |
+ raise CompileError( |
+ node.pos, "undeclared name not builtin: %s" % modname) |
+ scope = mod.as_module |
+ |
+ entry = scope.lookup(path[-1]) |
+ if not entry: |
+ raise CompileError(node.pos, "No such attribute '%s'" % path[-1]) |
+ |
+ return entry |
+ |
+# |
+### Utility loading |
+# |
+ |
+def load_memview_cy_utility(util_code_name, context=None, **kwargs): |
+ return CythonUtilityCode.load(util_code_name, "MemoryView.pyx", |
+ context=context, **kwargs) |
+ |
+def load_memview_c_utility(util_code_name, context=None, **kwargs): |
+ if context is None: |
+ return UtilityCode.load(util_code_name, "MemoryView_C.c", **kwargs) |
+ else: |
+ return TempitaUtilityCode.load(util_code_name, "MemoryView_C.c", |
+ context=context, **kwargs) |
+ |
+def use_cython_array_utility_code(env): |
+ cython_scope = env.global_scope().context.cython_scope |
+ cython_scope.load_cythonscope() |
+ cython_scope.viewscope.lookup('array_cwrapper').used = True |
+ |
+context = { |
+ 'memview_struct_name': memview_objstruct_cname, |
+ 'max_dims': Options.buffer_max_dims, |
+ 'memviewslice_name': memviewslice_cname, |
+ 'memslice_init': memslice_entry_init, |
+} |
+memviewslice_declare_code = load_memview_c_utility( |
+ "MemviewSliceStruct", |
+ proto_block='utility_code_proto_before_types', |
+ context=context, |
+ requires=[]) |
+ |
+atomic_utility = load_memview_c_utility("Atomics", context, |
+ proto_block='utility_code_proto_before_types') |
+ |
+memviewslice_init_code = load_memview_c_utility( |
+ "MemviewSliceInit", |
+ context=dict(context, BUF_MAX_NDIMS=Options.buffer_max_dims), |
+ requires=[memviewslice_declare_code, |
+ Buffer.acquire_utility_code, |
+ atomic_utility], |
+) |
+ |
+memviewslice_index_helpers = load_memview_c_utility("MemviewSliceIndex") |
+ |
+typeinfo_to_format_code = load_memview_cy_utility( |
+ "BufferFormatFromTypeInfo", requires=[Buffer._typeinfo_to_format_code]) |
+ |
+is_contig_utility = load_memview_c_utility("MemviewSliceIsContig", context) |
+overlapping_utility = load_memview_c_utility("OverlappingSlices", context) |
+copy_contents_new_utility = load_memview_c_utility( |
+ "MemviewSliceCopyTemplate", |
+ context, |
+ requires=[], # require cython_array_utility_code |
+) |
+ |
+view_utility_code = load_memview_cy_utility( |
+ "View.MemoryView", |
+ context=context, |
+ requires=[Buffer.GetAndReleaseBufferUtilityCode(), |
+ Buffer.buffer_struct_declare_code, |
+ Buffer.empty_bufstruct_utility, |
+ memviewslice_init_code, |
+ is_contig_utility, |
+ overlapping_utility, |
+ copy_contents_new_utility, |
+ ModuleNode.capsule_utility_code], |
+) |
+view_utility_whitelist = ('array', 'memoryview', 'array_cwrapper', |
+ 'generic', 'strided', 'indirect', 'contiguous', |
+ 'indirect_contiguous') |
+ |
+memviewslice_declare_code.requires.append(view_utility_code) |
+copy_contents_new_utility.requires.append(view_utility_code) |