1 from Errors
import CompileError
, error
3 from ExprNodes
import IntNode
, NameNode
, AttributeNode
5 from Code
import UtilityCode
, TempitaUtilityCode
6 from UtilityCode
import CythonUtilityCode
11 START_ERR
= "Start must not be given."
12 STOP_ERR
= "Axis specification only allowed in the 'step' slot."
13 STEP_ERR
= "Step must be omitted, 1, or a valid specifier."
14 BOTH_CF_ERR
= "Cannot specify an array that is both C and Fortran contiguous."
15 INVALID_ERR
= "Invalid axis specification."
16 NOT_CIMPORTED_ERR
= "Variable was not cimported from cython.view"
17 EXPR_ERR
= "no expressions allowed in axis spec, only names and literals."
18 CF_ERR
= "Invalid axis specification for a C/Fortran contiguous array."
19 ERR_UNINITIALIZED
= ("Cannot check if memoryview %s is initialized without the "
20 "GIL, consider using initializedcheck(False)")
22 def err_if_nogil_initialized_check(pos
, env
, name
='variable'):
23 "This raises an exception at runtime now"
25 #if env.nogil and env.directives['initializedcheck']:
26 #error(pos, ERR_UNINITIALIZED % name)
28 def concat_flags(*flags
):
29 return "(%s)" % "|".join(flags
)
31 format_flag
= "PyBUF_FORMAT"
33 memview_c_contiguous
= "(PyBUF_C_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)"
34 memview_f_contiguous
= "(PyBUF_F_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)"
35 memview_any_contiguous
= "(PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)"
36 memview_full_access
= "PyBUF_FULL"
37 #memview_strided_access = "PyBUF_STRIDED"
38 memview_strided_access
= "PyBUF_RECORDS"
40 MEMVIEW_DIRECT
= '__Pyx_MEMVIEW_DIRECT'
41 MEMVIEW_PTR
= '__Pyx_MEMVIEW_PTR'
42 MEMVIEW_FULL
= '__Pyx_MEMVIEW_FULL'
43 MEMVIEW_CONTIG
= '__Pyx_MEMVIEW_CONTIG'
44 MEMVIEW_STRIDED
= '__Pyx_MEMVIEW_STRIDED'
45 MEMVIEW_FOLLOW
= '__Pyx_MEMVIEW_FOLLOW'
48 'direct' : MEMVIEW_DIRECT
,
50 'full' : MEMVIEW_FULL
,
51 'contig' : MEMVIEW_CONTIG
,
52 'strided': MEMVIEW_STRIDED
,
53 'follow' : MEMVIEW_FOLLOW
,
65 memslice_entry_init
= "{ 0, 0, { 0 }, { 0 }, { 0 } }"
67 memview_name
= u
'memoryview'
68 memview_typeptr_cname
= '__pyx_memoryview_type'
69 memview_objstruct_cname
= '__pyx_memoryview_obj'
70 memviewslice_cname
= u
'__Pyx_memviewslice'
72 def put_init_entry(mv_cname
, code
):
73 code
.putln("%s.data = NULL;" % mv_cname
)
74 code
.putln("%s.memview = NULL;" % mv_cname
)
76 def mangle_dtype_name(dtype
):
77 # a dumb wrapper for now; move Buffer.mangle_dtype_name in here later?
79 return Buffer
.mangle_dtype_name(dtype
)
81 #def axes_to_str(axes):
82 # return "".join([access[0].upper()+packing[0] for (access, packing) in axes])
84 def put_acquire_memoryviewslice(lhs_cname
, lhs_type
, lhs_pos
, rhs
, code
,
85 have_gil
=False, first_assignment
=True):
86 "We can avoid decreffing the lhs if we know it is the first assignment"
87 assert rhs
.type.is_memoryviewslice
89 pretty_rhs
= rhs
.result_in_temp() or rhs
.is_simple()
93 rhstmp
= code
.funcstate
.allocate_temp(lhs_type
, manage_ref
=False)
94 code
.putln("%s = %s;" % (rhstmp
, rhs
.result_as(lhs_type
)))
96 # Allow uninitialized assignment
97 #code.putln(code.put_error_if_unbound(lhs_pos, rhs.entry))
98 put_assign_to_memviewslice(lhs_cname
, rhs
, rhstmp
, lhs_type
, code
,
99 have_gil
=have_gil
, first_assignment
=first_assignment
)
102 code
.funcstate
.release_temp(rhstmp
)
104 def put_assign_to_memviewslice(lhs_cname
, rhs
, rhs_cname
, memviewslicetype
, code
,
105 have_gil
=False, first_assignment
=False):
106 if not first_assignment
:
107 code
.put_xdecref_memoryviewslice(lhs_cname
, have_gil
=have_gil
)
109 if not rhs
.result_in_temp():
110 rhs
.make_owned_memoryviewslice(code
)
112 code
.putln("%s = %s;" % (lhs_cname
, rhs_cname
))
114 def get_buf_flags(specs
):
115 is_c_contig
, is_f_contig
= is_cf_contig(specs
)
118 return memview_c_contiguous
120 return memview_f_contiguous
122 access
, packing
= zip(*specs
)
124 if 'full' in access
or 'ptr' in access
:
125 return memview_full_access
127 return memview_strided_access
129 def insert_newaxes(memoryviewtype
, n
):
130 axes
= [('direct', 'strided')] * n
131 axes
.extend(memoryviewtype
.axes
)
132 return PyrexTypes
.MemoryViewSliceType(memoryviewtype
.dtype
, axes
)
134 def broadcast_types(src
, dst
):
135 n
= abs(src
.ndim
- dst
.ndim
)
136 if src
.ndim
< dst
.ndim
:
137 return insert_newaxes(src
, n
), dst
139 return src
, insert_newaxes(dst
, n
)
141 def src_conforms_to_dst(src
, dst
, broadcast
=False):
143 returns True if src conforms to dst, False otherwise.
145 If conformable, the types are the same, the ndims are equal, and each axis spec is conformable.
147 Any packing/access spec is conformable to itself.
149 'direct' and 'ptr' are conformable to 'full'.
150 'contig' and 'follow' are conformable to 'strided'.
151 Any other combo is not conformable.
154 if src
.dtype
!= dst
.dtype
:
157 if src
.ndim
!= dst
.ndim
:
159 src
, dst
= broadcast_types(src
, dst
)
163 for src_spec
, dst_spec
in zip(src
.axes
, dst
.axes
):
164 src_access
, src_packing
= src_spec
165 dst_access
, dst_packing
= dst_spec
166 if src_access
!= dst_access
and dst_access
!= 'full':
168 if src_packing
!= dst_packing
and dst_packing
!= 'strided':
173 def valid_memslice_dtype(dtype
, i
=0):
175 Return whether type dtype can be used as the base type of a
178 We support structs, numeric types and objects
180 if dtype
.is_complex
and dtype
.real_type
.is_int
:
183 if dtype
is PyrexTypes
.c_bint_type
:
186 if dtype
.is_struct
and dtype
.kind
== 'struct':
187 for member
in dtype
.scope
.var_entries
:
188 if not valid_memslice_dtype(member
.type):
195 # Pointers are not valid (yet)
196 # (dtype.is_ptr and valid_memslice_dtype(dtype.base_type)) or
197 (dtype
.is_array
and i
< 8 and
198 valid_memslice_dtype(dtype
.base_type
, i
+ 1)) or
201 dtype
.is_fused
or # accept this as it will be replaced by specializations later
202 (dtype
.is_typedef
and valid_memslice_dtype(dtype
.typedef_base_type
))
205 def validate_memslice_dtype(pos
, dtype
):
206 if not valid_memslice_dtype(dtype
):
207 error(pos
, "Invalid base type for memoryview slice: %s" % dtype
)
210 class MemoryViewSliceBufferEntry(Buffer
.BufferEntry
):
211 def __init__(self
, entry
):
213 self
.type = entry
.type
214 self
.cname
= entry
.cname
215 self
.buf_ptr
= "%s.data" % self
.cname
217 dtype
= self
.entry
.type.dtype
218 dtype
= PyrexTypes
.CPtrType(dtype
)
220 self
.buf_ptr_type
= dtype
222 def get_buf_suboffsetvars(self
):
223 return self
._for
_all
_ndim
("%s.suboffsets[%d]")
225 def get_buf_stridevars(self
):
226 return self
._for
_all
_ndim
("%s.strides[%d]")
228 def get_buf_shapevars(self
):
229 return self
._for
_all
_ndim
("%s.shape[%d]")
231 def generate_buffer_lookup_code(self
, code
, index_cnames
):
232 axes
= [(dim
, index_cnames
[dim
], access
, packing
)
233 for dim
, (access
, packing
) in enumerate(self
.type.axes
)]
234 return self
._generate
_buffer
_lookup
_code
(code
, axes
)
236 def _generate_buffer_lookup_code(self
, code
, axes
, cast_result
=True):
238 type_decl
= self
.type.dtype
.declaration_code("")
240 for dim
, index
, access
, packing
in axes
:
241 shape
= "%s.shape[%d]" % (self
.cname
, dim
)
242 stride
= "%s.strides[%d]" % (self
.cname
, dim
)
243 suboffset
= "%s.suboffsets[%d]" % (self
.cname
, dim
)
245 flag
= get_memoryview_flag(access
, packing
)
247 if flag
in ("generic", "generic_contiguous"):
248 # Note: we cannot do cast tricks to avoid stride multiplication
249 # for generic_contiguous, as we may have to do (dtype *)
250 # or (dtype **) arithmetic, we won't know which unless
251 # we check suboffsets
252 code
.globalstate
.use_utility_code(memviewslice_index_helpers
)
253 bufp
= ('__pyx_memviewslice_index_full(%s, %s, %s, %s)' %
254 (bufp
, index
, stride
, suboffset
))
256 elif flag
== "indirect":
257 bufp
= "(%s + %s * %s)" % (bufp
, index
, stride
)
258 bufp
= ("(*((char **) %s) + %s)" % (bufp
, suboffset
))
260 elif flag
== "indirect_contiguous":
261 # Note: we do char ** arithmetic
262 bufp
= "(*((char **) %s + %s) + %s)" % (bufp
, index
, suboffset
)
264 elif flag
== "strided":
265 bufp
= "(%s + %s * %s)" % (bufp
, index
, stride
)
268 assert flag
== 'contiguous', flag
269 bufp
= '((char *) (((%s *) %s) + %s))' % (type_decl
, bufp
, index
)
271 bufp
= '( /* dim=%d */ %s )' % (dim
, bufp
)
274 return "((%s *) %s)" % (type_decl
, bufp
)
278 def generate_buffer_slice_code(self
, code
, indices
, dst
, have_gil
,
279 have_slices
, directives
):
281 Slice a memoryviewslice.
283 indices - list of index nodes. If not a SliceNode, or NoneNode,
284 then it must be coercible to Py_ssize_t
286 Simply call __pyx_memoryview_slice_memviewslice with the right
292 def load_slice_util(name
, dict):
293 proto
, impl
= TempitaUtilityCode
.load_as_string(
294 name
, "MemoryView_C.c", context
=dict)
297 all_dimensions_direct
= True
298 for access
, packing
in self
.type.axes
:
299 if access
!= 'direct':
300 all_dimensions_direct
= False
303 no_suboffset_dim
= all_dimensions_direct
and not have_slices
304 if not no_suboffset_dim
:
305 suboffset_dim
= code
.funcstate
.allocate_temp(
306 PyrexTypes
.c_int_type
, False)
307 code
.putln("%s = -1;" % suboffset_dim
)
309 code
.putln("%(dst)s.data = %(src)s.data;" % locals())
310 code
.putln("%(dst)s.memview = %(src)s.memview;" % locals())
311 code
.put_incref_memoryviewslice(dst
)
314 for index
in indices
:
315 error_goto
= code
.error_goto(index
.pos
)
316 if not index
.is_none
:
318 access
, packing
= self
.type.axes
[dim
]
320 if isinstance(index
, ExprNodes
.SliceNode
):
321 # slice, unspecified dimension, or part of ellipsis
323 for s
in "start stop step".split():
324 idx
= getattr(index
, s
)
325 have_idx
= d
['have_' + s
] = not idx
.is_none
331 if (not d
['have_start'] and
332 not d
['have_stop'] and
334 # full slice (:), simply copy over the extent, stride
335 # and suboffset. Also update suboffset_dim if needed
337 code
.put(load_slice_util("SimpleSlice", d
))
339 code
.put(load_slice_util("ToughSlice", d
))
345 attribs
= [('shape', 1), ('strides', 0), ('suboffsets', -1)]
346 for attrib
, value
in attribs
:
347 code
.putln("%s.%s[%d] = %d;" % (dst
, attrib
, new_ndim
, value
))
355 if access
== 'direct':
359 generic
= (access
== 'full')
361 return error(index
.pos
,
362 "All preceding dimensions must be "
363 "indexed and not sliced")
365 wraparound
= int(directives
['wraparound'])
366 boundscheck
= int(directives
['boundscheck'])
368 code
.put(load_slice_util("SliceIndex", d
))
370 if not no_suboffset_dim
:
371 code
.funcstate
.release_temp(suboffset_dim
)
374 def empty_slice(pos
):
375 none
= ExprNodes
.NoneNode(pos
)
376 return ExprNodes
.SliceNode(pos
, start
=none
,
377 stop
=none
, step
=none
)
379 def unellipsify(indices
, newaxes
, ndim
):
381 seen_ellipsis
= False
384 n_indices
= len(indices
) - len(newaxes
)
386 for index
in indices
:
387 if isinstance(index
, ExprNodes
.EllipsisNode
):
389 full_slice
= empty_slice(index
.pos
)
392 result
.append(full_slice
)
394 nslices
= ndim
- n_indices
+ 1
395 result
.extend([full_slice
] * nslices
)
398 have_slices
= (have_slices
or
399 isinstance(index
, ExprNodes
.SliceNode
) or
403 result_length
= len(result
) - len(newaxes
)
404 if result_length
< ndim
:
406 nslices
= ndim
- result_length
407 result
.extend([empty_slice(indices
[-1].pos
)] * nslices
)
409 return have_slices
, result
411 def get_memoryview_flag(access
, packing
):
412 if access
== 'full' and packing
in ('strided', 'follow'):
414 elif access
== 'full' and packing
== 'contig':
415 return 'generic_contiguous'
416 elif access
== 'ptr' and packing
in ('strided', 'follow'):
418 elif access
== 'ptr' and packing
== 'contig':
419 return 'indirect_contiguous'
420 elif access
== 'direct' and packing
in ('strided', 'follow'):
423 assert (access
, packing
) == ('direct', 'contig'), (access
, packing
)
426 def get_is_contig_func_name(c_or_f
, ndim
):
427 return "__pyx_memviewslice_is_%s_contig%d" % (c_or_f
, ndim
)
429 def get_is_contig_utility(c_contig
, ndim
):
430 C
= dict(context
, ndim
=ndim
)
432 utility
= load_memview_c_utility("MemviewSliceIsCContig", C
,
433 requires
=[is_contig_utility
])
435 utility
= load_memview_c_utility("MemviewSliceIsFContig", C
,
436 requires
=[is_contig_utility
])
440 def copy_src_to_dst_cname():
441 return "__pyx_memoryview_copy_contents"
443 def verify_direct_dimensions(node
):
444 for access
, packing
in node
.type.axes
:
445 if access
!= 'direct':
446 error(self
.pos
, "All dimensions must be direct")
448 def copy_broadcast_memview_src_to_dst(src
, dst
, code
):
450 Copy the contents of slice src to slice dst. Does not support indirect
453 verify_direct_dimensions(src
)
454 verify_direct_dimensions(dst
)
456 code
.putln(code
.error_goto_if_neg(
457 "%s(%s, %s, %d, %d, %d)" % (copy_src_to_dst_cname(),
458 src
.result(), dst
.result(),
459 src
.type.ndim
, dst
.type.ndim
,
460 dst
.type.dtype
.is_pyobject
),
463 def get_1d_fill_scalar_func(type, code
):
465 type_decl
= dtype
.declaration_code("")
467 dtype_name
= mangle_dtype_name(dtype
)
468 context
= dict(dtype_name
=dtype_name
, type_decl
=type_decl
)
469 utility
= load_memview_c_utility("FillStrided1DScalar", context
)
470 code
.globalstate
.use_utility_code(utility
)
471 return '__pyx_fill_slice_%s' % dtype_name
473 def assign_scalar(dst
, scalar
, code
):
475 Assign a scalar to a slice. dst must be a temp, scalar will be assigned
476 to a correct type and not just something assignable.
478 verify_direct_dimensions(dst
)
479 dtype
= dst
.type.dtype
480 type_decl
= dtype
.declaration_code("")
481 slice_decl
= dst
.type.declaration_code("")
484 code
.putln("%s __pyx_temp_scalar = %s;" % (type_decl
, scalar
.result()))
485 if dst
.result_in_temp() or (dst
.base
.is_name
and
486 isinstance(dst
.index
, ExprNodes
.EllipsisNode
)):
487 dst_temp
= dst
.result()
489 code
.putln("%s __pyx_temp_slice = %s;" % (slice_decl
, dst
.result()))
490 dst_temp
= "__pyx_temp_slice"
492 # with slice_iter(dst.type, dst_temp, dst.type.ndim, code) as p:
493 slice_iter_obj
= slice_iter(dst
.type, dst_temp
, dst
.type.ndim
, code
)
494 p
= slice_iter_obj
.start_loops()
496 if dtype
.is_pyobject
:
497 code
.putln("Py_DECREF(*(PyObject **) %s);" % p
)
499 code
.putln("*((%s *) %s) = __pyx_temp_scalar;" % (type_decl
, p
))
501 if dtype
.is_pyobject
:
502 code
.putln("Py_INCREF(__pyx_temp_scalar);")
504 slice_iter_obj
.end_loops()
507 def slice_iter(slice_type
, slice_temp
, ndim
, code
):
508 if slice_type
.is_c_contig
or slice_type
.is_f_contig
:
509 return ContigSliceIter(slice_type
, slice_temp
, ndim
, code
)
511 return StridedSliceIter(slice_type
, slice_temp
, ndim
, code
)
513 class SliceIter(object):
514 def __init__(self
, slice_type
, slice_temp
, ndim
, code
):
515 self
.slice_type
= slice_type
516 self
.slice_temp
= slice_temp
520 class ContigSliceIter(SliceIter
):
521 def start_loops(self
):
525 type_decl
= self
.slice_type
.dtype
.declaration_code("")
527 total_size
= ' * '.join("%s.shape[%d]" % (self
.slice_temp
, i
)
528 for i
in range(self
.ndim
))
529 code
.putln("Py_ssize_t __pyx_temp_extent = %s;" % total_size
)
530 code
.putln("Py_ssize_t __pyx_temp_idx;")
531 code
.putln("%s *__pyx_temp_pointer = (%s *) %s.data;" % (
532 type_decl
, type_decl
, self
.slice_temp
))
533 code
.putln("for (__pyx_temp_idx = 0; "
534 "__pyx_temp_idx < __pyx_temp_extent; "
535 "__pyx_temp_idx++) {")
537 return "__pyx_temp_pointer"
540 self
.code
.putln("__pyx_temp_pointer += 1;")
542 self
.code
.end_block()
544 class StridedSliceIter(SliceIter
):
545 def start_loops(self
):
549 for i
in range(self
.ndim
):
550 t
= i
, self
.slice_temp
, i
551 code
.putln("Py_ssize_t __pyx_temp_extent_%d = %s.shape[%d];" % t
)
552 code
.putln("Py_ssize_t __pyx_temp_stride_%d = %s.strides[%d];" % t
)
553 code
.putln("char *__pyx_temp_pointer_%d;" % i
)
554 code
.putln("Py_ssize_t __pyx_temp_idx_%d;" % i
)
556 code
.putln("__pyx_temp_pointer_0 = %s.data;" % self
.slice_temp
)
558 for i
in range(self
.ndim
):
560 code
.putln("__pyx_temp_pointer_%d = __pyx_temp_pointer_%d;" % (i
, i
- 1))
562 code
.putln("for (__pyx_temp_idx_%d = 0; "
563 "__pyx_temp_idx_%d < __pyx_temp_extent_%d; "
564 "__pyx_temp_idx_%d++) {" % (i
, i
, i
, i
))
566 return "__pyx_temp_pointer_%d" % (self
.ndim
- 1)
570 for i
in range(self
.ndim
- 1, -1, -1):
571 code
.putln("__pyx_temp_pointer_%d += __pyx_temp_stride_%d;" % (i
, i
))
577 def copy_c_or_fortran_cname(memview
):
578 if memview
.is_c_contig
:
583 return "__pyx_memoryview_copy_slice_%s_%s" % (
584 memview
.specialization_suffix(), c_or_f
)
586 def get_copy_new_utility(pos
, from_memview
, to_memview
):
587 if from_memview
.dtype
!= to_memview
.dtype
:
588 return error(pos
, "dtypes must be the same!")
589 if len(from_memview
.axes
) != len(to_memview
.axes
):
590 return error(pos
, "number of dimensions must be same")
591 if not (to_memview
.is_c_contig
or to_memview
.is_f_contig
):
592 return error(pos
, "to_memview must be c or f contiguous.")
594 for (access
, packing
) in from_memview
.axes
:
595 if access
!= 'direct':
597 pos
, "cannot handle 'full' or 'ptr' access at this time.")
599 if to_memview
.is_c_contig
:
601 contig_flag
= memview_c_contiguous
602 elif to_memview
.is_f_contig
:
604 contig_flag
= memview_f_contiguous
606 return load_memview_c_utility(
607 "CopyContentsUtility",
611 dtype_decl
=to_memview
.dtype
.declaration_code(''),
612 contig_flag
=contig_flag
,
613 ndim
=to_memview
.ndim
,
614 func_cname
=copy_c_or_fortran_cname(to_memview
),
615 dtype_is_object
=int(to_memview
.dtype
.is_pyobject
)),
616 requires
=[copy_contents_new_utility
])
618 def get_axes_specs(env
, axes
):
620 get_axes_specs(env, axes) -> list of (access, packing) specs for each axis.
621 access is one of 'full', 'ptr' or 'direct'
622 packing is one of 'contig', 'strided' or 'follow'
625 cythonscope
= env
.global_scope().context
.cython_scope
626 cythonscope
.load_cythonscope()
627 viewscope
= cythonscope
.viewscope
629 access_specs
= tuple([viewscope
.lookup(name
)
630 for name
in ('full', 'direct', 'ptr')])
631 packing_specs
= tuple([viewscope
.lookup(name
)
632 for name
in ('contig', 'strided', 'follow')])
634 is_f_contig
, is_c_contig
= False, False
635 default_access
, default_packing
= 'direct', 'strided'
636 cf_access
, cf_packing
= default_access
, 'follow'
640 for idx
, axis
in enumerate(axes
):
641 if not axis
.start
.is_none
:
642 raise CompileError(axis
.start
.pos
, START_ERR
)
644 if not axis
.stop
.is_none
:
645 raise CompileError(axis
.stop
.pos
, STOP_ERR
)
647 if axis
.step
.is_none
:
648 axes_specs
.append((default_access
, default_packing
))
650 elif isinstance(axis
.step
, IntNode
):
651 # the packing for the ::1 axis is contiguous,
652 # all others are cf_packing.
653 if axis
.step
.compile_time_value(env
) != 1:
654 raise CompileError(axis
.step
.pos
, STEP_ERR
)
656 axes_specs
.append((cf_access
, 'cfcontig'))
658 elif isinstance(axis
.step
, (NameNode
, AttributeNode
)):
659 entry
= _get_resolved_spec(env
, axis
.step
)
660 if entry
.name
in view_constant_to_access_packing
:
661 axes_specs
.append(view_constant_to_access_packing
[entry
.name
])
663 raise CompilerError(axis
.step
.pos
, INVALID_ERR
)
666 raise CompileError(axis
.step
.pos
, INVALID_ERR
)
668 # First, find out if we have a ::1 somewhere
671 for idx
, (access
, packing
) in enumerate(axes_specs
):
672 if packing
== 'cfcontig':
674 raise CompileError(axis
.step
.pos
, BOTH_CF_ERR
)
677 axes_specs
[idx
] = (access
, 'contig')
681 # We have a ::1 somewhere, see if we're C or Fortran contiguous
682 if contig_dim
== len(axes
) - 1:
687 if contig_dim
and not axes_specs
[contig_dim
- 1][0] in ('full', 'ptr'):
688 raise CompileError(axes
[contig_dim
].pos
,
689 "Fortran contiguous specifier must follow an indirect dimension")
692 # Contiguous in the last dimension, find the last indirect dimension
694 for idx
, (access
, packing
) in enumerate(reversed(axes_specs
)):
695 if access
in ('ptr', 'full'):
696 contig_dim
= len(axes
) - idx
- 1
698 # Replace 'strided' with 'follow' for any dimension following the last
699 # indirect dimension, the first dimension or the dimension following
701 # int[::indirect, ::1, :, :]
703 # int[::indirect, :, :, ::1]
705 start
= contig_dim
+ 1
706 stop
= len(axes
) - is_c_contig
707 for idx
, (access
, packing
) in enumerate(axes_specs
[start
:stop
]):
708 idx
= contig_dim
+ 1 + idx
709 if access
!= 'direct':
710 raise CompileError(axes
[idx
].pos
,
711 "Indirect dimension may not follow "
712 "Fortran contiguous dimension")
713 if packing
== 'contig':
714 raise CompileError(axes
[idx
].pos
,
715 "Dimension may not be contiguous")
716 axes_specs
[idx
] = (access
, cf_packing
)
719 # For C contiguity, we need to fix the 'contig' dimension
721 a
, p
= axes_specs
[-1]
722 axes_specs
[-1] = a
, 'contig'
724 validate_axes_specs([axis
.start
.pos
for axis
in axes
],
731 def validate_axes(pos
, axes
):
732 if len(axes
) >= Options
.buffer_max_dims
:
733 error(pos
, "More dimensions than the maximum number"
734 " of buffer dimensions were used.")
745 def is_cf_contig(specs
):
746 is_c_contig
= is_f_contig
= False
748 if (len(specs
) == 1 and specs
== [('direct', 'contig')]):
751 elif (specs
[-1] == ('direct','contig') and
752 all([axis
== ('direct','follow') for axis
in specs
[:-1]])):
753 # c_contiguous: 'follow', 'follow', ..., 'follow', 'contig'
756 elif (len(specs
) > 1 and
757 specs
[0] == ('direct','contig') and
758 all([axis
== ('direct','follow') for axis
in specs
[1:]])):
759 # f_contiguous: 'contig', 'follow', 'follow', ..., 'follow'
762 return is_c_contig
, is_f_contig
765 is_c_contig
, is_f_contig
= is_cf_contig(specs
)
772 for access
, packing
in specs
:
773 if access
in ('ptr', 'full'):
778 view_constant_to_access_packing
= {
779 'generic': ('full', 'strided'),
780 'strided': ('direct', 'strided'),
781 'indirect': ('ptr', 'strided'),
782 'generic_contiguous': ('full', 'contig'),
783 'contiguous': ('direct', 'contig'),
784 'indirect_contiguous': ('ptr', 'contig'),
787 def validate_axes_specs(positions
, specs
, is_c_contig
, is_f_contig
):
789 packing_specs
= ('contig', 'strided', 'follow')
790 access_specs
= ('direct', 'ptr', 'full')
792 # is_c_contig, is_f_contig = is_cf_contig(specs)
794 has_contig
= has_follow
= has_strided
= has_generic_contig
= False
796 last_indirect_dimension
= -1
797 for idx
, (access
, packing
) in enumerate(specs
):
799 last_indirect_dimension
= idx
801 for idx
, pos
, (access
, packing
) in zip(xrange(len(specs
)), positions
, specs
):
803 if not (access
in access_specs
and
804 packing
in packing_specs
):
805 raise CompileError(pos
, "Invalid axes specification.")
807 if packing
== 'strided':
809 elif packing
== 'contig':
811 raise CompileError(pos
, "Only one direct contiguous "
812 "axis may be specified.")
814 valid_contig_dims
= last_indirect_dimension
+ 1, len(specs
) - 1
815 if idx
not in valid_contig_dims
and access
!= 'ptr':
816 if last_indirect_dimension
+ 1 != len(specs
) - 1:
817 dims
= "dimensions %d and %d" % valid_contig_dims
819 dims
= "dimension %d" % valid_contig_dims
[0]
821 raise CompileError(pos
, "Only %s may be contiguous and direct" % dims
)
823 has_contig
= access
!= 'ptr'
824 elif packing
== 'follow':
826 raise CompileError(pos
, "A memoryview cannot have both follow and strided axis specifiers.")
827 if not (is_c_contig
or is_f_contig
):
828 raise CompileError(pos
, "Invalid use of the follow specifier.")
830 if access
in ('ptr', 'full'):
833 def _get_resolved_spec(env
, spec
):
834 # spec must be a NameNode or an AttributeNode
835 if isinstance(spec
, NameNode
):
836 return _resolve_NameNode(env
, spec
)
837 elif isinstance(spec
, AttributeNode
):
838 return _resolve_AttributeNode(env
, spec
)
840 raise CompileError(spec
.pos
, INVALID_ERR
)
842 def _resolve_NameNode(env
, node
):
844 resolved_name
= env
.lookup(node
.name
).name
845 except AttributeError:
846 raise CompileError(node
.pos
, INVALID_ERR
)
848 viewscope
= env
.global_scope().context
.cython_scope
.viewscope
849 entry
= viewscope
.lookup(resolved_name
)
851 raise CompileError(node
.pos
, NOT_CIMPORTED_ERR
)
855 def _resolve_AttributeNode(env
, node
):
857 while isinstance(node
, AttributeNode
):
858 path
.insert(0, node
.attribute
)
860 if isinstance(node
, NameNode
):
861 path
.insert(0, node
.name
)
863 raise CompileError(node
.pos
, EXPR_ERR
)
865 # must be at least 1 module name, o/w not an AttributeNode.
869 for modname
in modnames
:
870 mod
= scope
.lookup(modname
)
871 if not mod
or not mod
.as_module
:
873 node
.pos
, "undeclared name not builtin: %s" % modname
)
874 scope
= mod
.as_module
876 entry
= scope
.lookup(path
[-1])
878 raise CompileError(node
.pos
, "No such attribute '%s'" % path
[-1])
886 def load_memview_cy_utility(util_code_name
, context
=None, **kwargs
):
887 return CythonUtilityCode
.load(util_code_name
, "MemoryView.pyx",
888 context
=context
, **kwargs
)
890 def load_memview_c_utility(util_code_name
, context
=None, **kwargs
):
892 return UtilityCode
.load(util_code_name
, "MemoryView_C.c", **kwargs
)
894 return TempitaUtilityCode
.load(util_code_name
, "MemoryView_C.c",
895 context
=context
, **kwargs
)
897 def use_cython_array_utility_code(env
):
898 cython_scope
= env
.global_scope().context
.cython_scope
899 cython_scope
.load_cythonscope()
900 cython_scope
.viewscope
.lookup('array_cwrapper').used
= True
903 'memview_struct_name': memview_objstruct_cname
,
904 'max_dims': Options
.buffer_max_dims
,
905 'memviewslice_name': memviewslice_cname
,
906 'memslice_init': memslice_entry_init
,
908 memviewslice_declare_code
= load_memview_c_utility(
909 "MemviewSliceStruct",
910 proto_block
='utility_code_proto_before_types',
914 atomic_utility
= load_memview_c_utility("Atomics", context
,
915 proto_block
='utility_code_proto_before_types')
917 memviewslice_init_code
= load_memview_c_utility(
919 context
=dict(context
, BUF_MAX_NDIMS
=Options
.buffer_max_dims
),
920 requires
=[memviewslice_declare_code
,
921 Buffer
.acquire_utility_code
,
925 memviewslice_index_helpers
= load_memview_c_utility("MemviewSliceIndex")
927 typeinfo_to_format_code
= load_memview_cy_utility(
928 "BufferFormatFromTypeInfo", requires
=[Buffer
._typeinfo
_to
_format
_code
])
930 is_contig_utility
= load_memview_c_utility("MemviewSliceIsContig", context
)
931 overlapping_utility
= load_memview_c_utility("OverlappingSlices", context
)
932 copy_contents_new_utility
= load_memview_c_utility(
933 "MemviewSliceCopyTemplate",
935 requires
=[], # require cython_array_utility_code
938 view_utility_code
= load_memview_cy_utility(
941 requires
=[Buffer
.GetAndReleaseBufferUtilityCode(),
942 Buffer
.buffer_struct_declare_code
,
943 Buffer
.empty_bufstruct_utility
,
944 memviewslice_init_code
,
947 copy_contents_new_utility
,
948 ModuleNode
.capsule_utility_code
],
950 view_utility_whitelist
= ('array', 'memoryview', 'array_cwrapper',
951 'generic', 'strided', 'indirect', 'contiguous',
952 'indirect_contiguous')
954 memviewslice_declare_code
.requires
.append(view_utility_code
)
955 copy_contents_new_utility
.requires
.append(view_utility_code
)