from __future__ import absolute_import from .Errors import CompileError, error from . import ExprNodes from .ExprNodes import IntNode, NameNode, AttributeNode from . import Options from .Code import UtilityCode, TempitaUtilityCode from .UtilityCode import CythonUtilityCode from . import Buffer from . import PyrexTypes from . 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)") format_flag = "PyBUF_FORMAT" memview_c_contiguous = "(PyBUF_C_CONTIGUOUS | PyBUF_FORMAT)" memview_f_contiguous = "(PyBUF_F_CONTIGUOUS | PyBUF_FORMAT)" memview_any_contiguous = "(PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT)" memview_full_access = "PyBUF_FULL_RO" #memview_strided_access = "PyBUF_STRIDED_RO" memview_strided_access = "PyBUF_RECORDS_RO" 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 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 lhs_cname == rhs_cname: # self assignment is tricky because memoryview xdecref clears the memoryview # thus invalidating both sides of the assignment. Therefore make it actually do nothing code.putln("/* memoryview self assignment no-op */") return if not first_assignment: code.put_xdecref(lhs_cname, memviewslicetype, 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 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)) ) class MemoryViewSliceBufferEntry(Buffer.BufferEntry): """ May be used during code generation time to be queried for shape/strides/suboffsets attributes, or to perform indexing or slicing. """ 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 self.buf_ptr_type = PyrexTypes.CPtrType(dtype) self.init_attributes() 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): """ Generate a single expression that indexes the memory view slice in each dimension. """ bufp = self.buf_ptr type_decl = self.type.dtype.empty_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, dst_type, 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, unless the dimension is omitted or a bare ':', in which case we copy over the shape/strides/suboffsets attributes directly for that dimension. """ src = self.cname code.putln("%(dst)s.data = %(src)s.data;" % locals()) code.putln("%(dst)s.memview = %(src)s.memview;" % locals()) code.put_incref_memoryviewslice(dst, dst_type, have_gil=have_gil) all_dimensions_direct = all(access == 'direct' for access, packing in self.type.axes) suboffset_dim_temp = [] def get_suboffset_dim(): # create global temp variable at request if not suboffset_dim_temp: suboffset_dim = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False) code.putln("%s = -1;" % suboffset_dim) suboffset_dim_temp.append(suboffset_dim) return suboffset_dim_temp[0] dim = -1 new_ndim = 0 for index in indices: if index.is_none: # newaxis for attrib, value in [('shape', 1), ('strides', 0), ('suboffsets', -1)]: code.putln("%s.%s[%d] = %d;" % (dst, attrib, new_ndim, value)) new_ndim += 1 continue dim += 1 access, packing = self.type.axes[dim] if index.is_slice: # slice, unspecified dimension, or part of ellipsis d = dict(locals()) for s in "start stop step".split(): idx = getattr(index, s) have_idx = d['have_' + s] = not idx.is_none d[s] = idx.result() if have_idx else "0" if not (d['have_start'] or d['have_stop'] or d['have_step']): # full slice (:), simply copy over the extent, stride # and suboffset. Also update suboffset_dim if needed d['access'] = access util_name = "SimpleSlice" else: util_name = "ToughSlice" d['error_goto'] = code.error_goto(index.pos) new_ndim += 1 else: # normal index idx = index.result() indirect = access != 'direct' if indirect: generic = access == 'full' if new_ndim != 0: return error(index.pos, "All preceding dimensions must be " "indexed and not sliced") d = dict( locals(), wraparound=int(directives['wraparound']), boundscheck=int(directives['boundscheck']), ) if d['boundscheck']: d['error_goto'] = code.error_goto(index.pos) util_name = "SliceIndex" _, impl = TempitaUtilityCode.load_as_string(util_name, "MemoryView_C.c", context=d) code.put(impl) if suboffset_dim_temp: code.funcstate.release_temp(suboffset_dim_temp[0]) def empty_slice(pos): none = ExprNodes.NoneNode(pos) return ExprNodes.SliceNode(pos, start=none, stop=none, step=none) def unellipsify(indices, ndim): result = [] seen_ellipsis = False have_slices = False newaxes = [newaxis for newaxis in indices if newaxis.is_none] 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 index.is_slice 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, newaxes 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(contig_type, ndim): assert contig_type in ('C', 'F') return "__pyx_memviewslice_is_contig_%s%d" % (contig_type, ndim) def get_is_contig_utility(contig_type, ndim): assert contig_type in ('C', 'F') C = dict(context, ndim=ndim, contig_type=contig_type) utility = load_memview_c_utility("MemviewSliceCheckContig", C, requires=[is_contig_utility]) return utility def slice_iter(slice_type, slice_result, ndim, code, force_strided=False): if (slice_type.is_c_contig or slice_type.is_f_contig) and not force_strided: return ContigSliceIter(slice_type, slice_result, ndim, code) else: return StridedSliceIter(slice_type, slice_result, ndim, code) class SliceIter(object): def __init__(self, slice_type, slice_result, ndim, code): self.slice_type = slice_type self.slice_result = slice_result 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.empty_declaration_code() total_size = ' * '.join("%s.shape[%d]" % (self.slice_result, 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_result)) 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_result, 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_result) 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 and not (from_memview.dtype.is_cv_qualified and from_memview.dtype.cv_base_type == to_memview.dtype)): error(pos, "dtypes must be the same!") return if len(from_memview.axes) != len(to_memview.axes): error(pos, "number of dimensions must be same") return if not (to_memview.is_c_contig or to_memview.is_f_contig): error(pos, "to_memview must be c or f contiguous.") return for (access, packing) in from_memview.axes: if access != 'direct': error(pos, "cannot handle 'full' or 'ptr' access at this time.") return if to_memview.is_c_contig: mode = 'c' contig_flag = memview_c_contiguous else: assert 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.empty_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 CompileError(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 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 enumerate(zip(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': PyrexTypes.MemoryViewSliceType.default_value, 'THREAD_LOCKS_PREALLOCATED': 8, } memviewslice_declare_code = load_memview_c_utility( "MemviewSliceStruct", context=context, requires=[]) atomic_utility = load_memview_c_utility("Atomics", context) memviewslice_init_code = load_memview_c_utility( "MemviewSliceInit", context=dict(context, BUF_MAX_NDIMS=Options.buffer_max_dims), requires=[memviewslice_declare_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.buffer_formats_declare_code, memviewslice_init_code, is_contig_utility, overlapping_utility, copy_contents_new_utility, ], ) view_utility_allowlist = ('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)