1from __future__ import absolute_import
2
3from .Errors import CompileError, error
4from . import ExprNodes
5from .ExprNodes import IntNode, NameNode, AttributeNode
6from . import Options
7from .Code import UtilityCode, TempitaUtilityCode
8from .UtilityCode import CythonUtilityCode
9from . import Buffer
10from . import PyrexTypes
11from . import ModuleNode
12
13START_ERR = "Start must not be given."
14STOP_ERR = "Axis specification only allowed in the 'step' slot."
15STEP_ERR = "Step must be omitted, 1, or a valid specifier."
16BOTH_CF_ERR = "Cannot specify an array that is both C and Fortran contiguous."
17INVALID_ERR = "Invalid axis specification."
18NOT_CIMPORTED_ERR = "Variable was not cimported from cython.view"
19EXPR_ERR = "no expressions allowed in axis spec, only names and literals."
20CF_ERR = "Invalid axis specification for a C/Fortran contiguous array."
21ERR_UNINITIALIZED = ("Cannot check if memoryview %s is initialized without the "
22                     "GIL, consider using initializedcheck(False)")
23
24
25def concat_flags(*flags):
26    return "(%s)" % "|".join(flags)
27
28
29format_flag = "PyBUF_FORMAT"
30
31memview_c_contiguous = "(PyBUF_C_CONTIGUOUS | PyBUF_FORMAT)"
32memview_f_contiguous = "(PyBUF_F_CONTIGUOUS | PyBUF_FORMAT)"
33memview_any_contiguous = "(PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT)"
34memview_full_access = "PyBUF_FULL_RO"
35#memview_strided_access = "PyBUF_STRIDED_RO"
36memview_strided_access = "PyBUF_RECORDS_RO"
37
38MEMVIEW_DIRECT = '__Pyx_MEMVIEW_DIRECT'
39MEMVIEW_PTR    = '__Pyx_MEMVIEW_PTR'
40MEMVIEW_FULL   = '__Pyx_MEMVIEW_FULL'
41MEMVIEW_CONTIG = '__Pyx_MEMVIEW_CONTIG'
42MEMVIEW_STRIDED= '__Pyx_MEMVIEW_STRIDED'
43MEMVIEW_FOLLOW = '__Pyx_MEMVIEW_FOLLOW'
44
45_spec_to_const = {
46        'direct' : MEMVIEW_DIRECT,
47        'ptr'    : MEMVIEW_PTR,
48        'full'   : MEMVIEW_FULL,
49        'contig' : MEMVIEW_CONTIG,
50        'strided': MEMVIEW_STRIDED,
51        'follow' : MEMVIEW_FOLLOW,
52        }
53
54_spec_to_abbrev = {
55    'direct'  : 'd',
56    'ptr'     : 'p',
57    'full'    : 'f',
58    'contig'  : 'c',
59    'strided' : 's',
60    'follow'  : '_',
61}
62
63memslice_entry_init = "{ 0, 0, { 0 }, { 0 }, { 0 } }"
64
65memview_name = u'memoryview'
66memview_typeptr_cname = '__pyx_memoryview_type'
67memview_objstruct_cname = '__pyx_memoryview_obj'
68memviewslice_cname = u'__Pyx_memviewslice'
69
70
71def put_init_entry(mv_cname, code):
72    code.putln("%s.data = NULL;" % mv_cname)
73    code.putln("%s.memview = NULL;" % mv_cname)
74
75
76#def axes_to_str(axes):
77#    return "".join([access[0].upper()+packing[0] for (access, packing) in axes])
78
79
80def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code,
81                                have_gil=False, first_assignment=True):
82    "We can avoid decreffing the lhs if we know it is the first assignment"
83    assert rhs.type.is_memoryviewslice
84
85    pretty_rhs = rhs.result_in_temp() or rhs.is_simple()
86    if pretty_rhs:
87        rhstmp = rhs.result()
88    else:
89        rhstmp = code.funcstate.allocate_temp(lhs_type, manage_ref=False)
90        code.putln("%s = %s;" % (rhstmp, rhs.result_as(lhs_type)))
91
92    # Allow uninitialized assignment
93    #code.putln(code.put_error_if_unbound(lhs_pos, rhs.entry))
94    put_assign_to_memviewslice(lhs_cname, rhs, rhstmp, lhs_type, code,
95                               have_gil=have_gil, first_assignment=first_assignment)
96
97    if not pretty_rhs:
98        code.funcstate.release_temp(rhstmp)
99
100
101def put_assign_to_memviewslice(lhs_cname, rhs, rhs_cname, memviewslicetype, code,
102                               have_gil=False, first_assignment=False):
103    if not first_assignment:
104        code.put_xdecref_memoryviewslice(lhs_cname, have_gil=have_gil)
105
106    if not rhs.result_in_temp():
107        rhs.make_owned_memoryviewslice(code)
108
109    code.putln("%s = %s;" % (lhs_cname, rhs_cname))
110
111
112def get_buf_flags(specs):
113    is_c_contig, is_f_contig = is_cf_contig(specs)
114
115    if is_c_contig:
116        return memview_c_contiguous
117    elif is_f_contig:
118        return memview_f_contiguous
119
120    access, packing = zip(*specs)
121
122    if 'full' in access or 'ptr' in access:
123        return memview_full_access
124    else:
125        return memview_strided_access
126
127
128def insert_newaxes(memoryviewtype, n):
129    axes = [('direct', 'strided')] * n
130    axes.extend(memoryviewtype.axes)
131    return PyrexTypes.MemoryViewSliceType(memoryviewtype.dtype, axes)
132
133
134def broadcast_types(src, dst):
135    n = abs(src.ndim - dst.ndim)
136    if src.ndim < dst.ndim:
137        return insert_newaxes(src, n), dst
138    else:
139        return src, insert_newaxes(dst, n)
140
141
142def valid_memslice_dtype(dtype, i=0):
143    """
144    Return whether type dtype can be used as the base type of a
145    memoryview slice.
146
147    We support structs, numeric types and objects
148    """
149    if dtype.is_complex and dtype.real_type.is_int:
150        return False
151
152    if dtype is PyrexTypes.c_bint_type:
153        return False
154
155    if dtype.is_struct and dtype.kind == 'struct':
156        for member in dtype.scope.var_entries:
157            if not valid_memslice_dtype(member.type):
158                return False
159
160        return True
161
162    return (
163        dtype.is_error or
164        # Pointers are not valid (yet)
165        # (dtype.is_ptr and valid_memslice_dtype(dtype.base_type)) or
166        (dtype.is_array and i < 8 and
167         valid_memslice_dtype(dtype.base_type, i + 1)) or
168        dtype.is_numeric or
169        dtype.is_pyobject or
170        dtype.is_fused or # accept this as it will be replaced by specializations later
171        (dtype.is_typedef and valid_memslice_dtype(dtype.typedef_base_type))
172    )
173
174
175class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
176    """
177    May be used during code generation time to be queried for
178    shape/strides/suboffsets attributes, or to perform indexing or slicing.
179    """
180    def __init__(self, entry):
181        self.entry = entry
182        self.type = entry.type
183        self.cname = entry.cname
184
185        self.buf_ptr = "%s.data" % self.cname
186
187        dtype = self.entry.type.dtype
188        self.buf_ptr_type = PyrexTypes.CPtrType(dtype)
189        self.init_attributes()
190
191    def get_buf_suboffsetvars(self):
192        return self._for_all_ndim("%s.suboffsets[%d]")
193
194    def get_buf_stridevars(self):
195        return self._for_all_ndim("%s.strides[%d]")
196
197    def get_buf_shapevars(self):
198        return self._for_all_ndim("%s.shape[%d]")
199
200    def generate_buffer_lookup_code(self, code, index_cnames):
201        axes = [(dim, index_cnames[dim], access, packing)
202                    for dim, (access, packing) in enumerate(self.type.axes)]
203        return self._generate_buffer_lookup_code(code, axes)
204
205    def _generate_buffer_lookup_code(self, code, axes, cast_result=True):
206        """
207        Generate a single expression that indexes the memory view slice
208        in each dimension.
209        """
210        bufp = self.buf_ptr
211        type_decl = self.type.dtype.empty_declaration_code()
212
213        for dim, index, access, packing in axes:
214            shape = "%s.shape[%d]" % (self.cname, dim)
215            stride = "%s.strides[%d]" % (self.cname, dim)
216            suboffset = "%s.suboffsets[%d]" % (self.cname, dim)
217
218            flag = get_memoryview_flag(access, packing)
219
220            if flag in ("generic", "generic_contiguous"):
221                # Note: we cannot do cast tricks to avoid stride multiplication
222                #       for generic_contiguous, as we may have to do (dtype *)
223                #       or (dtype **) arithmetic, we won't know which unless
224                #       we check suboffsets
225                code.globalstate.use_utility_code(memviewslice_index_helpers)
226                bufp = ('__pyx_memviewslice_index_full(%s, %s, %s, %s)' %
227                                            (bufp, index, stride, suboffset))
228
229            elif flag == "indirect":
230                bufp = "(%s + %s * %s)" % (bufp, index, stride)
231                bufp = ("(*((char **) %s) + %s)" % (bufp, suboffset))
232
233            elif flag == "indirect_contiguous":
234                # Note: we do char ** arithmetic
235                bufp = "(*((char **) %s + %s) + %s)" % (bufp, index, suboffset)
236
237            elif flag == "strided":
238                bufp = "(%s + %s * %s)" % (bufp, index, stride)
239
240            else:
241                assert flag == 'contiguous', flag
242                bufp = '((char *) (((%s *) %s) + %s))' % (type_decl, bufp, index)
243
244            bufp = '( /* dim=%d */ %s )' % (dim, bufp)
245
246        if cast_result:
247            return "((%s *) %s)" % (type_decl, bufp)
248
249        return bufp
250
251    def generate_buffer_slice_code(self, code, indices, dst, have_gil,
252                                   have_slices, directives):
253        """
254        Slice a memoryviewslice.
255
256        indices     - list of index nodes. If not a SliceNode, or NoneNode,
257                      then it must be coercible to Py_ssize_t
258
259        Simply call __pyx_memoryview_slice_memviewslice with the right
260        arguments, unless the dimension is omitted or a bare ':', in which
261        case we copy over the shape/strides/suboffsets attributes directly
262        for that dimension.
263        """
264        src = self.cname
265
266        code.putln("%(dst)s.data = %(src)s.data;" % locals())
267        code.putln("%(dst)s.memview = %(src)s.memview;" % locals())
268        code.put_incref_memoryviewslice(dst)
269
270        all_dimensions_direct = all(access == 'direct' for access, packing in self.type.axes)
271        suboffset_dim_temp = []
272
273        def get_suboffset_dim():
274            # create global temp variable at request
275            if not suboffset_dim_temp:
276                suboffset_dim = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
277                code.putln("%s = -1;" % suboffset_dim)
278                suboffset_dim_temp.append(suboffset_dim)
279            return suboffset_dim_temp[0]
280
281        dim = -1
282        new_ndim = 0
283        for index in indices:
284            if index.is_none:
285                # newaxis
286                for attrib, value in [('shape', 1), ('strides', 0), ('suboffsets', -1)]:
287                    code.putln("%s.%s[%d] = %d;" % (dst, attrib, new_ndim, value))
288
289                new_ndim += 1
290                continue
291
292            dim += 1
293            access, packing = self.type.axes[dim]
294
295            if isinstance(index, ExprNodes.SliceNode):
296                # slice, unspecified dimension, or part of ellipsis
297                d = dict(locals())
298                for s in "start stop step".split():
299                    idx = getattr(index, s)
300                    have_idx = d['have_' + s] = not idx.is_none
301                    d[s] = idx.result() if have_idx else "0"
302
303                if not (d['have_start'] or d['have_stop'] or d['have_step']):
304                    # full slice (:), simply copy over the extent, stride
305                    # and suboffset. Also update suboffset_dim if needed
306                    d['access'] = access
307                    util_name = "SimpleSlice"
308                else:
309                    util_name = "ToughSlice"
310                    d['error_goto'] = code.error_goto(index.pos)
311
312                new_ndim += 1
313            else:
314                # normal index
315                idx = index.result()
316
317                indirect = access != 'direct'
318                if indirect:
319                    generic = access == 'full'
320                    if new_ndim != 0:
321                        return error(index.pos,
322                                     "All preceding dimensions must be "
323                                     "indexed and not sliced")
324
325                d = dict(
326                    locals(),
327                    wraparound=int(directives['wraparound']),
328                    boundscheck=int(directives['boundscheck']),
329                )
330                if d['boundscheck']:
331                    d['error_goto'] = code.error_goto(index.pos)
332                util_name = "SliceIndex"
333
334            _, impl = TempitaUtilityCode.load_as_string(util_name, "MemoryView_C.c", context=d)
335            code.put(impl)
336
337        if suboffset_dim_temp:
338            code.funcstate.release_temp(suboffset_dim_temp[0])
339
340
341def empty_slice(pos):
342    none = ExprNodes.NoneNode(pos)
343    return ExprNodes.SliceNode(pos, start=none,
344                               stop=none, step=none)
345
346
347def unellipsify(indices, ndim):
348    result = []
349    seen_ellipsis = False
350    have_slices = False
351
352    newaxes = [newaxis for newaxis in indices if newaxis.is_none]
353    n_indices = len(indices) - len(newaxes)
354
355    for index in indices:
356        if isinstance(index, ExprNodes.EllipsisNode):
357            have_slices = True
358            full_slice = empty_slice(index.pos)
359
360            if seen_ellipsis:
361                result.append(full_slice)
362            else:
363                nslices = ndim - n_indices + 1
364                result.extend([full_slice] * nslices)
365                seen_ellipsis = True
366        else:
367            have_slices = have_slices or index.is_slice or index.is_none
368            result.append(index)
369
370    result_length = len(result) - len(newaxes)
371    if result_length < ndim:
372        have_slices = True
373        nslices = ndim - result_length
374        result.extend([empty_slice(indices[-1].pos)] * nslices)
375
376    return have_slices, result, newaxes
377
378
379def get_memoryview_flag(access, packing):
380    if access == 'full' and packing in ('strided', 'follow'):
381        return 'generic'
382    elif access == 'full' and packing == 'contig':
383        return 'generic_contiguous'
384    elif access == 'ptr' and packing in ('strided', 'follow'):
385        return 'indirect'
386    elif access == 'ptr' and packing == 'contig':
387        return 'indirect_contiguous'
388    elif access == 'direct' and packing in ('strided', 'follow'):
389        return 'strided'
390    else:
391        assert (access, packing) == ('direct', 'contig'), (access, packing)
392        return 'contiguous'
393
394
395def get_is_contig_func_name(contig_type, ndim):
396    assert contig_type in ('C', 'F')
397    return "__pyx_memviewslice_is_contig_%s%d" % (contig_type, ndim)
398
399
400def get_is_contig_utility(contig_type, ndim):
401    assert contig_type in ('C', 'F')
402    C = dict(context, ndim=ndim, contig_type=contig_type)
403    utility = load_memview_c_utility("MemviewSliceCheckContig", C, requires=[is_contig_utility])
404    return utility
405
406
407def slice_iter(slice_type, slice_result, ndim, code):
408    if slice_type.is_c_contig or slice_type.is_f_contig:
409        return ContigSliceIter(slice_type, slice_result, ndim, code)
410    else:
411        return StridedSliceIter(slice_type, slice_result, ndim, code)
412
413
414class SliceIter(object):
415    def __init__(self, slice_type, slice_result, ndim, code):
416        self.slice_type = slice_type
417        self.slice_result = slice_result
418        self.code = code
419        self.ndim = ndim
420
421
422class ContigSliceIter(SliceIter):
423    def start_loops(self):
424        code = self.code
425        code.begin_block()
426
427        type_decl = self.slice_type.dtype.empty_declaration_code()
428
429        total_size = ' * '.join("%s.shape[%d]" % (self.slice_result, i)
430                                for i in range(self.ndim))
431        code.putln("Py_ssize_t __pyx_temp_extent = %s;" % total_size)
432        code.putln("Py_ssize_t __pyx_temp_idx;")
433        code.putln("%s *__pyx_temp_pointer = (%s *) %s.data;" % (
434            type_decl, type_decl, self.slice_result))
435        code.putln("for (__pyx_temp_idx = 0; "
436                        "__pyx_temp_idx < __pyx_temp_extent; "
437                        "__pyx_temp_idx++) {")
438
439        return "__pyx_temp_pointer"
440
441    def end_loops(self):
442        self.code.putln("__pyx_temp_pointer += 1;")
443        self.code.putln("}")
444        self.code.end_block()
445
446
447class StridedSliceIter(SliceIter):
448    def start_loops(self):
449        code = self.code
450        code.begin_block()
451
452        for i in range(self.ndim):
453            t = i, self.slice_result, i
454            code.putln("Py_ssize_t __pyx_temp_extent_%d = %s.shape[%d];" % t)
455            code.putln("Py_ssize_t __pyx_temp_stride_%d = %s.strides[%d];" % t)
456            code.putln("char *__pyx_temp_pointer_%d;" % i)
457            code.putln("Py_ssize_t __pyx_temp_idx_%d;" % i)
458
459        code.putln("__pyx_temp_pointer_0 = %s.data;" % self.slice_result)
460
461        for i in range(self.ndim):
462            if i > 0:
463                code.putln("__pyx_temp_pointer_%d = __pyx_temp_pointer_%d;" % (i, i - 1))
464
465            code.putln("for (__pyx_temp_idx_%d = 0; "
466                            "__pyx_temp_idx_%d < __pyx_temp_extent_%d; "
467                            "__pyx_temp_idx_%d++) {" % (i, i, i, i))
468
469        return "__pyx_temp_pointer_%d" % (self.ndim - 1)
470
471    def end_loops(self):
472        code = self.code
473        for i in range(self.ndim - 1, -1, -1):
474            code.putln("__pyx_temp_pointer_%d += __pyx_temp_stride_%d;" % (i, i))
475            code.putln("}")
476
477        code.end_block()
478
479
480def copy_c_or_fortran_cname(memview):
481    if memview.is_c_contig:
482        c_or_f = 'c'
483    else:
484        c_or_f = 'f'
485
486    return "__pyx_memoryview_copy_slice_%s_%s" % (
487            memview.specialization_suffix(), c_or_f)
488
489
490def get_copy_new_utility(pos, from_memview, to_memview):
491    if (from_memview.dtype != to_memview.dtype and
492            not (from_memview.dtype.is_const and from_memview.dtype.const_base_type == to_memview.dtype)):
493        error(pos, "dtypes must be the same!")
494        return
495    if len(from_memview.axes) != len(to_memview.axes):
496        error(pos, "number of dimensions must be same")
497        return
498    if not (to_memview.is_c_contig or to_memview.is_f_contig):
499        error(pos, "to_memview must be c or f contiguous.")
500        return
501
502    for (access, packing) in from_memview.axes:
503        if access != 'direct':
504            error(pos, "cannot handle 'full' or 'ptr' access at this time.")
505            return
506
507    if to_memview.is_c_contig:
508        mode = 'c'
509        contig_flag = memview_c_contiguous
510    elif to_memview.is_f_contig:
511        mode = 'fortran'
512        contig_flag = memview_f_contiguous
513
514    return load_memview_c_utility(
515        "CopyContentsUtility",
516        context=dict(
517            context,
518            mode=mode,
519            dtype_decl=to_memview.dtype.empty_declaration_code(),
520            contig_flag=contig_flag,
521            ndim=to_memview.ndim,
522            func_cname=copy_c_or_fortran_cname(to_memview),
523            dtype_is_object=int(to_memview.dtype.is_pyobject)),
524        requires=[copy_contents_new_utility])
525
526
527def get_axes_specs(env, axes):
528    '''
529    get_axes_specs(env, axes) -> list of (access, packing) specs for each axis.
530    access is one of 'full', 'ptr' or 'direct'
531    packing is one of 'contig', 'strided' or 'follow'
532    '''
533
534    cythonscope = env.global_scope().context.cython_scope
535    cythonscope.load_cythonscope()
536    viewscope = cythonscope.viewscope
537
538    access_specs = tuple([viewscope.lookup(name)
539                    for name in ('full', 'direct', 'ptr')])
540    packing_specs = tuple([viewscope.lookup(name)
541                    for name in ('contig', 'strided', 'follow')])
542
543    is_f_contig, is_c_contig = False, False
544    default_access, default_packing = 'direct', 'strided'
545    cf_access, cf_packing = default_access, 'follow'
546
547    axes_specs = []
548    # analyse all axes.
549    for idx, axis in enumerate(axes):
550        if not axis.start.is_none:
551            raise CompileError(axis.start.pos,  START_ERR)
552
553        if not axis.stop.is_none:
554            raise CompileError(axis.stop.pos, STOP_ERR)
555
556        if axis.step.is_none:
557            axes_specs.append((default_access, default_packing))
558
559        elif isinstance(axis.step, IntNode):
560            # the packing for the ::1 axis is contiguous,
561            # all others are cf_packing.
562            if axis.step.compile_time_value(env) != 1:
563                raise CompileError(axis.step.pos, STEP_ERR)
564
565            axes_specs.append((cf_access, 'cfcontig'))
566
567        elif isinstance(axis.step, (NameNode, AttributeNode)):
568            entry = _get_resolved_spec(env, axis.step)
569            if entry.name in view_constant_to_access_packing:
570                axes_specs.append(view_constant_to_access_packing[entry.name])
571            else:
572                raise CompileError(axis.step.pos, INVALID_ERR)
573
574        else:
575            raise CompileError(axis.step.pos, INVALID_ERR)
576
577    # First, find out if we have a ::1 somewhere
578    contig_dim = 0
579    is_contig = False
580    for idx, (access, packing) in enumerate(axes_specs):
581        if packing == 'cfcontig':
582            if is_contig:
583                raise CompileError(axis.step.pos, BOTH_CF_ERR)
584
585            contig_dim = idx
586            axes_specs[idx] = (access, 'contig')
587            is_contig = True
588
589    if is_contig:
590        # We have a ::1 somewhere, see if we're C or Fortran contiguous
591        if contig_dim == len(axes) - 1:
592            is_c_contig = True
593        else:
594            is_f_contig = True
595
596            if contig_dim and not axes_specs[contig_dim - 1][0] in ('full', 'ptr'):
597                raise CompileError(axes[contig_dim].pos,
598                                   "Fortran contiguous specifier must follow an indirect dimension")
599
600        if is_c_contig:
601            # Contiguous in the last dimension, find the last indirect dimension
602            contig_dim = -1
603            for idx, (access, packing) in enumerate(reversed(axes_specs)):
604                if access in ('ptr', 'full'):
605                    contig_dim = len(axes) - idx - 1
606
607        # Replace 'strided' with 'follow' for any dimension following the last
608        # indirect dimension, the first dimension or the dimension following
609        # the ::1.
610        #               int[::indirect, ::1, :, :]
611        #                                    ^  ^
612        #               int[::indirect, :, :, ::1]
613        #                               ^  ^
614        start = contig_dim + 1
615        stop = len(axes) - is_c_contig
616        for idx, (access, packing) in enumerate(axes_specs[start:stop]):
617            idx = contig_dim + 1 + idx
618            if access != 'direct':
619                raise CompileError(axes[idx].pos,
620                                   "Indirect dimension may not follow "
621                                   "Fortran contiguous dimension")
622            if packing == 'contig':
623                raise CompileError(axes[idx].pos,
624                                   "Dimension may not be contiguous")
625            axes_specs[idx] = (access, cf_packing)
626
627        if is_c_contig:
628            # For C contiguity, we need to fix the 'contig' dimension
629            # after the loop
630            a, p = axes_specs[-1]
631            axes_specs[-1] = a, 'contig'
632
633    validate_axes_specs([axis.start.pos for axis in axes],
634                        axes_specs,
635                        is_c_contig,
636                        is_f_contig)
637
638    return axes_specs
639
640
641def validate_axes(pos, axes):
642    if len(axes) >= Options.buffer_max_dims:
643        error(pos, "More dimensions than the maximum number"
644                   " of buffer dimensions were used.")
645        return False
646
647    return True
648
649
650def is_cf_contig(specs):
651    is_c_contig = is_f_contig = False
652
653    if len(specs) == 1 and specs == [('direct', 'contig')]:
654        is_c_contig = True
655
656    elif (specs[-1] == ('direct','contig') and
657          all(axis == ('direct','follow') for axis in specs[:-1])):
658        # c_contiguous: 'follow', 'follow', ..., 'follow', 'contig'
659        is_c_contig = True
660
661    elif (len(specs) > 1 and
662        specs[0] == ('direct','contig') and
663        all(axis == ('direct','follow') for axis in specs[1:])):
664        # f_contiguous: 'contig', 'follow', 'follow', ..., 'follow'
665        is_f_contig = True
666
667    return is_c_contig, is_f_contig
668
669
670def get_mode(specs):
671    is_c_contig, is_f_contig = is_cf_contig(specs)
672
673    if is_c_contig:
674        return 'c'
675    elif is_f_contig:
676        return 'fortran'
677
678    for access, packing in specs:
679        if access in ('ptr', 'full'):
680            return 'full'
681
682    return 'strided'
683
684view_constant_to_access_packing = {
685    'generic':              ('full',   'strided'),
686    'strided':              ('direct', 'strided'),
687    'indirect':             ('ptr',    'strided'),
688    'generic_contiguous':   ('full',   'contig'),
689    'contiguous':           ('direct', 'contig'),
690    'indirect_contiguous':  ('ptr',    'contig'),
691}
692
693def validate_axes_specs(positions, specs, is_c_contig, is_f_contig):
694
695    packing_specs = ('contig', 'strided', 'follow')
696    access_specs = ('direct', 'ptr', 'full')
697
698    # is_c_contig, is_f_contig = is_cf_contig(specs)
699
700    has_contig = has_follow = has_strided = has_generic_contig = False
701
702    last_indirect_dimension = -1
703    for idx, (access, packing) in enumerate(specs):
704        if access == 'ptr':
705            last_indirect_dimension = idx
706
707    for idx, (pos, (access, packing)) in enumerate(zip(positions, specs)):
708
709        if not (access in access_specs and
710                packing in packing_specs):
711            raise CompileError(pos, "Invalid axes specification.")
712
713        if packing == 'strided':
714            has_strided = True
715        elif packing == 'contig':
716            if has_contig:
717                raise CompileError(pos, "Only one direct contiguous "
718                                        "axis may be specified.")
719
720            valid_contig_dims = last_indirect_dimension + 1, len(specs) - 1
721            if idx not in valid_contig_dims and access != 'ptr':
722                if last_indirect_dimension + 1 != len(specs) - 1:
723                    dims = "dimensions %d and %d" % valid_contig_dims
724                else:
725                    dims = "dimension %d" % valid_contig_dims[0]
726
727                raise CompileError(pos, "Only %s may be contiguous and direct" % dims)
728
729            has_contig = access != 'ptr'
730        elif packing == 'follow':
731            if has_strided:
732                raise CompileError(pos, "A memoryview cannot have both follow and strided axis specifiers.")
733            if not (is_c_contig or is_f_contig):
734                raise CompileError(pos, "Invalid use of the follow specifier.")
735
736        if access in ('ptr', 'full'):
737            has_strided = False
738
739def _get_resolved_spec(env, spec):
740    # spec must be a NameNode or an AttributeNode
741    if isinstance(spec, NameNode):
742        return _resolve_NameNode(env, spec)
743    elif isinstance(spec, AttributeNode):
744        return _resolve_AttributeNode(env, spec)
745    else:
746        raise CompileError(spec.pos, INVALID_ERR)
747
748def _resolve_NameNode(env, node):
749    try:
750        resolved_name = env.lookup(node.name).name
751    except AttributeError:
752        raise CompileError(node.pos, INVALID_ERR)
753
754    viewscope = env.global_scope().context.cython_scope.viewscope
755    entry = viewscope.lookup(resolved_name)
756    if entry is None:
757        raise CompileError(node.pos, NOT_CIMPORTED_ERR)
758
759    return entry
760
761def _resolve_AttributeNode(env, node):
762    path = []
763    while isinstance(node, AttributeNode):
764        path.insert(0, node.attribute)
765        node = node.obj
766    if isinstance(node, NameNode):
767        path.insert(0, node.name)
768    else:
769        raise CompileError(node.pos, EXPR_ERR)
770    modnames = path[:-1]
771    # must be at least 1 module name, o/w not an AttributeNode.
772    assert modnames
773
774    scope = env
775    for modname in modnames:
776        mod = scope.lookup(modname)
777        if not mod or not mod.as_module:
778            raise CompileError(
779                    node.pos, "undeclared name not builtin: %s" % modname)
780        scope = mod.as_module
781
782    entry = scope.lookup(path[-1])
783    if not entry:
784        raise CompileError(node.pos, "No such attribute '%s'" % path[-1])
785
786    return entry
787
788#
789### Utility loading
790#
791
792def load_memview_cy_utility(util_code_name, context=None, **kwargs):
793    return CythonUtilityCode.load(util_code_name, "MemoryView.pyx",
794                                  context=context, **kwargs)
795
796def load_memview_c_utility(util_code_name, context=None, **kwargs):
797    if context is None:
798        return UtilityCode.load(util_code_name, "MemoryView_C.c", **kwargs)
799    else:
800        return TempitaUtilityCode.load(util_code_name, "MemoryView_C.c",
801                                       context=context, **kwargs)
802
803def use_cython_array_utility_code(env):
804    cython_scope = env.global_scope().context.cython_scope
805    cython_scope.load_cythonscope()
806    cython_scope.viewscope.lookup('array_cwrapper').used = True
807
808context = {
809    'memview_struct_name': memview_objstruct_cname,
810    'max_dims': Options.buffer_max_dims,
811    'memviewslice_name': memviewslice_cname,
812    'memslice_init': memslice_entry_init,
813}
814memviewslice_declare_code = load_memview_c_utility(
815        "MemviewSliceStruct",
816        context=context,
817        requires=[])
818
819atomic_utility = load_memview_c_utility("Atomics", context)
820
821memviewslice_init_code = load_memview_c_utility(
822    "MemviewSliceInit",
823    context=dict(context, BUF_MAX_NDIMS=Options.buffer_max_dims),
824    requires=[memviewslice_declare_code,
825              atomic_utility],
826)
827
828memviewslice_index_helpers = load_memview_c_utility("MemviewSliceIndex")
829
830typeinfo_to_format_code = load_memview_cy_utility(
831        "BufferFormatFromTypeInfo", requires=[Buffer._typeinfo_to_format_code])
832
833is_contig_utility = load_memview_c_utility("MemviewSliceIsContig", context)
834overlapping_utility = load_memview_c_utility("OverlappingSlices", context)
835copy_contents_new_utility = load_memview_c_utility(
836    "MemviewSliceCopyTemplate",
837    context,
838    requires=[], # require cython_array_utility_code
839)
840
841view_utility_code = load_memview_cy_utility(
842        "View.MemoryView",
843        context=context,
844        requires=[Buffer.GetAndReleaseBufferUtilityCode(),
845                  Buffer.buffer_struct_declare_code,
846                  Buffer.buffer_formats_declare_code,
847                  memviewslice_init_code,
848                  is_contig_utility,
849                  overlapping_utility,
850                  copy_contents_new_utility,
851                  ModuleNode.capsule_utility_code],
852)
853view_utility_whitelist = ('array', 'memoryview', 'array_cwrapper',
854                          'generic', 'strided', 'indirect', 'contiguous',
855                          'indirect_contiguous')
856
857memviewslice_declare_code.requires.append(view_utility_code)
858copy_contents_new_utility.requires.append(view_utility_code)
859