Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Gwenaël Samain
cython
Commits
155a4ef5
Commit
155a4ef5
authored
Aug 09, 2011
by
Mark Florisson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support slicing memoryview slices
parent
917cce5e
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
505 additions
and
85 deletions
+505
-85
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+139
-27
Cython/Compiler/MemoryView.py
Cython/Compiler/MemoryView.py
+225
-14
Cython/Compiler/Pipeline.py
Cython/Compiler/Pipeline.py
+5
-2
Cython/Compiler/PyrexTypes.py
Cython/Compiler/PyrexTypes.py
+15
-4
Cython/Compiler/UtilityCode.py
Cython/Compiler/UtilityCode.py
+1
-1
Cython/Utility/MemoryView_C.c
Cython/Utility/MemoryView_C.c
+4
-7
tests/run/cythonarray.pyx
tests/run/cythonarray.pyx
+2
-21
tests/run/cythonarrayutil.pxi
tests/run/cythonarrayutil.pxi
+26
-0
tests/run/memslice.pyx
tests/run/memslice.pyx
+88
-9
No files found.
Cython/Compiler/ExprNodes.py
View file @
155a4ef5
...
...
@@ -2266,6 +2266,10 @@ class IndexNode(ExprNode):
# writable)
writable_needed
=
False
# Whether we are indexing or slicing a memoryviewslice
memslice_index
=
False
memslice_slice
=
False
def
__init__
(
self
,
pos
,
index
,
*
args
,
**
kw
):
ExprNode
.
__init__
(
self
,
pos
,
index
=
index
,
*
args
,
**
kw
)
self
.
_index
=
index
...
...
@@ -2286,8 +2290,11 @@ class IndexNode(ExprNode):
return
self
.
base
.
is_ephemeral
()
def
is_simple
(
self
):
if
self
.
is_buffer_access
:
if
self
.
is_buffer_access
or
self
.
memslice_index
:
return
False
elif
self
.
memslice_slice
:
return
True
base
=
self
.
base
return
(
base
.
is_simple
()
and
self
.
index
.
is_simple
()
and
base
.
type
and
(
base
.
type
.
is_ptr
or
base
.
type
.
is_array
))
...
...
@@ -2377,7 +2384,13 @@ class IndexNode(ExprNode):
# For buffers, self.index is packed out on the initial analysis, and
# when cloning self.indices is copied.
self
.
is_buffer_access
=
False
# a[...] = b
self
.
is_memoryviewslice_access
=
False
# incomplete indexing, Ellipsis indexing or slicing
self
.
memslice_slice
=
False
# integer indexing
self
.
memslice_index
=
False
self
.
base
.
analyse_types
(
env
)
if
self
.
base
.
type
.
is_error
:
...
...
@@ -2402,18 +2415,84 @@ class IndexNode(ExprNode):
is_memslice
=
self
.
base
.
type
.
is_memoryviewslice
if
self
.
indices
:
indices
=
self
.
indices
elif
isinstance
(
self
.
index
,
TupleNode
):
indices
=
self
.
index
.
args
else
:
indices
=
[
self
.
index
]
if
(
is_memslice
and
not
self
.
indices
and
isinstance
(
self
.
index
,
EllipsisNode
)):
# Memoryviewslice copying
memoryviewslice_access
=
True
elif
self
.
base
.
type
.
is_buffer
or
is_memslice
:
if
self
.
indices
:
indices
=
self
.
indices
else
:
if
isinstance
(
self
.
index
,
TupleNode
):
indices
=
self
.
index
.
args
elif
is_memslice
:
# memoryviewslice indexing or slicing
import
MemoryView
skip_child_analysis
=
True
indices
=
MemoryView
.
unellipsify
(
indices
,
self
.
base
.
type
.
ndim
)
self
.
memslice_index
=
len
(
indices
)
==
self
.
base
.
type
.
ndim
axes
=
[]
index_type
=
PyrexTypes
.
c_py_ssize_t_type
new_indices
=
[]
if
len
(
indices
)
>
self
.
base
.
type
.
ndim
:
self
.
type
=
error_type
return
error
(
indices
[
self
.
base
.
type
.
ndim
].
pos
,
"Too many indices specified for type %s"
%
self
.
base
.
type
)
suboffsets_dim
=
-
1
for
i
,
index
in
enumerate
(
indices
[:]):
index
.
analyse_types
(
env
)
access
,
packing
=
self
.
base
.
type
.
axes
[
i
]
if
isinstance
(
index
,
SliceNode
):
suboffsets_dim
=
i
self
.
memslice_slice
=
True
axes
.
append
((
access
,
'strided'
))
# Coerce start, stop and step to temps of the right type
for
attr
in
(
'start'
,
'stop'
,
'step'
):
value
=
getattr
(
index
,
attr
)
if
not
value
.
is_none
:
value
=
value
.
coerce_to
(
index_type
,
env
)
value
=
value
.
coerce_to_temp
(
env
)
setattr
(
index
,
attr
,
value
)
new_indices
.
append
(
value
)
elif
index
.
type
.
is_int
:
self
.
memslice_index
=
True
index
=
index
.
coerce_to
(
index_type
,
env
).
coerce_to_temp
(
index_type
)
indices
[
i
]
=
index
new_indices
.
append
(
index
)
if
access
in
(
'ptr'
,
'generic'
)
and
i
!=
0
:
# If this dimension is to disappear, then how do we
# indicate that we need to dereference in this dimension
# if the previous dimension is already indirect, or if
# the previous dimension was direct but also indexed?
# Basically only a[i, j, k, :] can work, as you can
# set the base pointer to start in the fourth dimension
self
.
type
=
error_type
return
error
(
index
.
pos
,
"Indexing of non-leading indirect or generic "
"dimensions not supported yet, "
"try slicing with i:i+1"
)
else
:
indices
=
[
self
.
index
]
self
.
type
=
error_type
return
error
(
index
.
pos
,
"Invalid index for memoryview specified"
)
self
.
memslice_index
=
self
.
memslice_index
and
not
self
.
memslice_slice
self
.
original_indices
=
indices
self
.
indices
=
new_indices
elif
self
.
base
.
type
.
is_buffer
:
# Buffer indexing
if
len
(
indices
)
==
self
.
base
.
type
.
ndim
:
buffer_access
=
True
skip_child_analysis
=
True
...
...
@@ -2430,7 +2509,7 @@ class IndexNode(ExprNode):
self
.
nogil
=
env
.
nogil
if
buffer_access
:
if
buffer_access
or
self
.
memslice_index
:
if
self
.
base
.
type
.
is_memoryviewslice
and
not
self
.
base
.
is_name
:
self
.
base
=
self
.
base
.
coerce_to_temp
(
env
)
...
...
@@ -2459,6 +2538,11 @@ class IndexNode(ExprNode):
if
getting
:
error
(
self
.
pos
,
"memoryviews currently support setting only."
)
elif
self
.
memslice_slice
:
self
.
is_temp
=
True
self
.
type
=
PyrexTypes
.
MemoryViewSliceType
(
self
.
base
.
type
.
dtype
,
axes
)
else
:
base_type
=
self
.
base
.
type
if
isinstance
(
self
.
index
,
TupleNode
):
...
...
@@ -2533,7 +2617,7 @@ class IndexNode(ExprNode):
gil_message
=
"Indexing Python object"
def
nogil_check
(
self
,
env
):
if
self
.
is_buffer_access
:
if
self
.
is_buffer_access
or
self
.
memslice_index
:
if
env
.
directives
[
'boundscheck'
]:
error
(
self
.
pos
,
"Cannot check buffer index bounds without gil; use boundscheck(False) directive"
)
return
...
...
@@ -2599,7 +2683,7 @@ class IndexNode(ExprNode):
i
.
free_temps
(
code
)
def
generate_result_code
(
self
,
code
):
if
self
.
is_buffer_access
:
if
self
.
is_buffer_access
or
self
.
memslice_index
:
if
code
.
globalstate
.
directives
[
'nonecheck'
]:
self
.
put_nonecheck
(
code
)
buffer_entry
,
self
.
buffer_ptr_code
=
self
.
buffer_lookup_code
(
code
)
...
...
@@ -2607,6 +2691,10 @@ class IndexNode(ExprNode):
# is_temp is True, so must pull out value and incref it.
code
.
putln
(
"%s = *%s;"
%
(
self
.
result
(),
self
.
buffer_ptr_code
))
code
.
putln
(
"__Pyx_INCREF((PyObject*)%s);"
%
self
.
result
())
elif
self
.
memslice_slice
:
self
.
put_memoryviewslice_slice_code
(
code
)
elif
self
.
is_temp
:
if
self
.
type
.
is_pyobject
:
if
self
.
index
.
type
.
is_int
:
...
...
@@ -2677,7 +2765,7 @@ class IndexNode(ExprNode):
self
.
extra_index_params
(),
code
.
error_goto
(
self
.
pos
)))
def
generate_memoryviewslice_
setitem
_code
(
self
,
rhs
,
code
,
op
=
""
):
def
generate_memoryviewslice_
copy
_code
(
self
,
rhs
,
code
,
op
=
""
):
assert
isinstance
(
self
.
index
,
EllipsisNode
)
import
MemoryView
util_code
=
MemoryView
.
CopyContentsFuncUtilCode
(
rhs
.
type
,
self
.
type
)
...
...
@@ -2687,7 +2775,7 @@ class IndexNode(ExprNode):
def
generate_buffer_setitem_code
(
self
,
rhs
,
code
,
op
=
""
):
# Used from generate_assignment_code and InPlaceAssignmentNode
if
code
.
globalstate
.
directives
[
'nonecheck'
]:
if
code
.
globalstate
.
directives
[
'nonecheck'
]
and
not
self
.
memslice_index
:
self
.
put_nonecheck
(
code
)
buffer_entry
,
ptrexpr
=
self
.
buffer_lookup_code
(
code
)
...
...
@@ -2712,10 +2800,13 @@ class IndexNode(ExprNode):
def
generate_assignment_code
(
self
,
rhs
,
code
):
self
.
generate_subexpr_evaluation_code
(
code
)
if
self
.
is_buffer_access
:
if
self
.
is_buffer_access
or
self
.
memslice_index
:
self
.
generate_buffer_setitem_code
(
rhs
,
code
)
elif
self
.
memslice_slice
:
error
(
rhs
.
pos
,
"Slice assignment not supported yet"
)
#self.generate_memoryviewslice_setslice_code(rhs, code)
elif
self
.
is_memoryviewslice_access
:
self
.
generate_memoryviewslice_
setitem
_code
(
rhs
,
code
)
self
.
generate_memoryviewslice_
copy
_code
(
rhs
,
code
)
elif
self
.
type
.
is_pyobject
:
self
.
generate_setitem_code
(
rhs
.
py_result
(),
code
)
else
:
...
...
@@ -2750,6 +2841,23 @@ class IndexNode(ExprNode):
self
.
generate_subexpr_disposal_code
(
code
)
self
.
free_subexpr_temps
(
code
)
def
buffer_entry
(
self
):
import
Buffer
,
MemoryView
if
self
.
base
.
is_name
:
entry
=
self
.
base
.
entry
else
:
assert
self
.
base
.
is_temp
cname
=
self
.
base
.
result
()
entry
=
Symtab
.
Entry
(
cname
,
cname
,
self
.
base
.
type
,
self
.
base
.
pos
)
if
entry
.
type
.
is_buffer
:
buffer_entry
=
Buffer
.
BufferEntry
(
entry
)
else
:
buffer_entry
=
MemoryView
.
MemoryViewSliceBufferEntry
(
entry
)
return
buffer_entry
def
buffer_lookup_code
(
self
,
code
):
# Assign indices to temps
index_temps
=
[
code
.
funcstate
.
allocate_temp
(
i
.
type
,
manage_ref
=
False
)
...
...
@@ -2761,21 +2869,13 @@ class IndexNode(ExprNode):
# Generate buffer access code using these temps
import
Buffer
,
MemoryView
if
self
.
base
.
is_name
:
entry
=
self
.
base
.
entry
else
:
assert
self
.
base
.
is_temp
cname
=
self
.
base
.
result
()
entry
=
Symtab
.
Entry
(
cname
,
cname
,
self
.
base
.
type
,
self
.
base
.
pos
)
buffer_entry
=
self
.
buffer_entry
()
if
entry
.
type
.
is_buffer
:
buffer_entry
=
Buffer
.
BufferEntry
(
entry
)
if
buffer_entry
.
type
.
is_buffer
:
negative_indices
=
entry
.
type
.
negative_indices
else
:
buffer_entry
=
MemoryView
.
MemoryViewSliceBufferEntry
(
entry
)
negative_indices
=
Buffer
.
buffer_defaults
[
'negative_indices'
]
return
buffer_entry
,
Buffer
.
put_buffer_lookup_code
(
entry
=
buffer_entry
,
index_signeds
=
[
i
.
type
.
signed
for
i
in
self
.
indices
],
...
...
@@ -2784,6 +2884,14 @@ class IndexNode(ExprNode):
pos
=
self
.
pos
,
code
=
code
,
negative_indices
=
negative_indices
)
def
put_memoryviewslice_slice_code
(
self
,
code
):
buffer_entry
=
self
.
buffer_entry
()
buffer_entry
.
generate_buffer_slice_code
(
code
,
self
.
original_indices
,
self
.
base
.
type
,
self
.
type
,
self
.
result
())
def
put_nonecheck
(
self
,
code
):
code
.
globalstate
.
use_utility_code
(
raise_noneindex_error_utility_code
)
code
.
putln
(
"if (%s) {"
%
code
.
unlikely
(
"%s == Py_None"
)
%
self
.
base
.
result_as
(
PyrexTypes
.
py_object_type
))
...
...
@@ -2791,6 +2899,7 @@ class IndexNode(ExprNode):
code
.
putln
(
code
.
error_goto
(
self
.
pos
))
code
.
putln
(
"}"
)
class
SliceIndexNode
(
ExprNode
):
# 2-element slice indexing
#
...
...
@@ -3928,6 +4037,9 @@ class AttributeNode(ExprNode):
if
obj_type
.
has_attributes
:
entry
=
None
if
obj_type
.
attributes_known
():
if
(
obj_type
.
is_memoryviewslice
and
not
obj_type
.
scope
.
lookup_here
(
self
.
attribute
)):
obj_type
.
declare_attribute
(
self
.
attribute
)
entry
=
obj_type
.
scope
.
lookup_here
(
self
.
attribute
)
if
entry
and
entry
.
is_member
:
entry
=
None
...
...
@@ -7384,8 +7496,8 @@ class CmpNode(object):
contains_utility_code
=
UtilityCode
(
proto
=
"""
static CYTHON_INLINE int __Pyx_NegateNonNeg(int b) {
return unlikely(b < 0) ? b : !b;
static CYTHON_INLINE int __Pyx_NegateNonNeg(int b) {
return unlikely(b < 0) ? b : !b;
}
static CYTHON_INLINE PyObject* __Pyx_PyBoolOrNull_FromLong(long b) {
return unlikely(b < 0) ? NULL : __Pyx_PyBool_FromLong(b);
...
...
Cython/Compiler/MemoryView.py
View file @
155a4ef5
...
...
@@ -104,11 +104,11 @@ def put_assign_to_memviewslice(lhs_cname, rhs_cname, memviewslicetype, code,
code
.
putln
(
"%s.memview = %s.memview;"
%
(
lhs_cname
,
rhs_cname
))
code
.
putln
(
"%s.data = %s.data;"
%
(
lhs_cname
,
rhs_cname
))
ndim
=
len
(
memviewslicetype
.
axes
)
for
i
in
range
(
ndim
):
code
.
putln
(
"%s.shape[%d] = %s.shape[%d];"
%
(
lhs_cname
,
i
,
rhs_cname
,
i
)
)
code
.
putln
(
"%s.strides[%d] = %s.strides[%d];"
%
(
lhs_cname
,
i
,
rhs_cname
,
i
)
)
code
.
putln
(
"%s.suboffsets[%d] = %s.suboffsets[%d];"
%
(
lhs_cname
,
i
,
rhs_cname
,
i
)
)
for
i
in
range
(
memviewslicetype
.
ndim
):
tup
=
(
lhs_cname
,
i
,
rhs_cname
,
i
)
code
.
putln
(
"%s.shape[%d] = %s.shape[%d];"
%
tup
)
code
.
putln
(
"%s.strides[%d] = %s.strides[%d];"
%
tup
)
code
.
putln
(
"%s.suboffsets[%d] = %s.suboffsets[%d];"
%
tup
)
def
get_buf_flags
(
specs
):
is_c_contig
,
is_f_contig
=
is_cf_contig
(
specs
)
...
...
@@ -182,14 +182,18 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
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
,
(
access
,
packing
)
in
enumerate
(
self
.
type
.
axes
)
:
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
)
index
=
index_cnames
[
dim
]
flag
=
get_memoryview_flag
(
access
,
packing
)
...
...
@@ -219,7 +223,214 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
bufp
=
'( /* dim=%d */ %s )'
%
(
dim
,
bufp
)
return
"((%s *) %s)"
%
(
type_decl
,
bufp
)
if
cast_result
:
return
"((%s *) %s)"
%
(
type_decl
,
bufp
)
return
bufp
def
generate_buffer_slice_code
(
self
,
code
,
indices
,
type
,
dst_type
,
dst
):
"""
Generate code for a new memoryview slice.
indices An index in the indices list is either a SliceNode where
start/stop and step are converted to temps of type Py_ssize_t, or a
temp integer index of type Py_ssize_t.
type is the type that is sliced
dst_type is the resulting type
dst is the result memoryview slice temp
For every dimension we do the following:
ensure 0 <= start < n and 0 <= stop <= n
if every dimension is direct:
keep track of possible offsets, set data * to
mslice[0, 0, 0, ...] after the loop
else:
keep a variable 'dim' that
if < 0
indicates the data *
else
indicates suboffsets[dim]
now set shape, strides and suboffsets according to start/stop/step
add the slice or indexing offset to data * or to suboffsets[dim]
update the dim variable if necessary
"""
axes
=
[]
temps
=
[]
dtype
=
PyrexTypes
.
c_py_ssize_t_type
def
put_bounds_code
(
r
,
start
):
"ensure that start 0 <= r < n"
code
.
putln
(
"if (%s < 0) {"
%
r
)
code
.
putln
(
"%s += %s;"
%
(
r
,
shape
))
code
.
putln
(
"if (%s < 0) %s = 0;"
%
(
r
,
r
))
if
start
:
code
.
putln
(
"} else if (%s >= %s) %s = %s - 1;"
%
(
r
,
shape
,
r
,
shape
))
else
:
code
.
putln
(
"} else if (%s > %s) %s = %s;"
%
(
r
,
shape
,
r
,
shape
))
def
update_dim
():
"if we are indirect, update our dim index"
if
access
in
(
'ptr'
,
'generic'
):
if
access
==
'generic'
:
code
.
put
(
"if (%s >= 0)"
%
suboffset
)
code
.
putln
(
"%s = %d;"
%
(
offset_dim
,
dst_dim
))
all_direct
=
True
for
access
,
packing
in
type
.
axes
:
all_direct
=
all_direct
and
access
==
'direct'
if
not
all_direct
:
break
if
not
all_direct
:
offset_dim
=
code
.
funcstate
.
allocate_temp
(
PyrexTypes
.
c_int_type
,
False
)
code
.
putln
(
"%s = -1;"
%
(
offset_dim
,))
code
.
putln
(
"%s.data = %s.data;"
%
(
dst
,
self
.
cname
))
dst_dim
=
0
for
dim
,
index
in
enumerate
(
indices
):
goto_err
=
code
.
error_goto
(
index
.
pos
)
shape
=
"%s.shape[%d]"
%
(
self
.
cname
,
dim
)
stride
=
"%s.strides[%d]"
%
(
self
.
cname
,
dim
)
suboffset
=
"%s.suboffsets[%d]"
%
(
self
.
cname
,
dim
)
dst_shape
=
"%s.shape[%d]"
%
(
dst
,
dst_dim
)
dst_stride
=
"%s.strides[%d]"
%
(
dst
,
dst_dim
)
dst_suboffset
=
"%s.suboffsets[%d]"
%
(
dst
,
dst_dim
)
access
,
packing
=
type
.
axes
[
dim
]
if
isinstance
(
index
,
ExprNodes
.
SliceNode
):
# slice or part of ellipsis
start
=
stop
=
step
=
None
dst_dim
+=
1
# First fix the bounds
if
not
index
.
start
.
is_none
:
start
=
index
.
start
.
result
()
put_bounds_code
(
start
,
start
=
True
)
if
not
index
.
stop
.
is_none
:
stop
=
index
.
stop
.
result
()
put_bounds_code
(
stop
,
start
=
False
)
# Compute the new strides
if
not
index
.
step
.
is_none
:
step
=
index
.
step
.
result
()
code
.
putln
(
"%s = %s * %s;"
%
(
dst_stride
,
stride
,
step
))
else
:
code
.
putln
(
"%s = %s;"
%
(
dst_stride
,
stride
))
# Take care of suboffsets
code
.
putln
(
"%s = %s;"
%
(
dst_suboffset
,
suboffset
))
# If start or stop is not specified, then we need to set
# them according to step
if
not
start
or
not
stop
:
if
step
:
code
.
putln
(
"if (%s > 0) {"
%
step
)
if
not
start
:
start
=
code
.
funcstate
.
allocate_temp
(
dtype
,
False
)
temps
.
append
(
start
)
code
.
putln
(
"%s = 0;"
%
start
)
if
not
stop
:
stop
=
code
.
funcstate
.
allocate_temp
(
dtype
,
False
)
temps
.
append
(
stop
)
code
.
putln
(
"%s = %s;"
%
(
stop
,
shape
))
code
.
putln
(
"} else {"
)
if
start
:
code
.
putln
(
"%s = %s - 1;"
%
(
start
,
shape
))
if
stop
:
code
.
putln
(
"%s = -1;"
%
stop
)
code
.
putln
(
"}"
)
else
:
if
not
start
:
start
=
"0"
if
not
stop
:
stop
=
shape
d
=
dict
(
locals
(),
step
=
step
or
"1"
)
# Take care of shape
code
.
putln
(
"%(dst_shape)s = (%(stop)s - %(start)s) / %(step)s;"
%
d
)
code
.
putln
(
"if (%(dst_shape)s && (%(stop)s - %(start)s) %% %(step)s) %(dst_shape)s++;"
%
d
)
code
.
putln
(
"if (%(dst_shape)s < 0) %(dst_shape)s = 0;"
%
d
)
# Take care of slicing offsets
if
start
!=
"0"
:
if
all_direct
:
axes
.
append
([
dim
,
start
,
access
,
packing
])
else
:
offset
=
"%s * %s"
%
(
start
,
stride
)
d
=
dict
(
dim
=
offset_dim
,
dst
=
dst
,
offset
=
offset
)
code
.
putln
(
"if (%(dim)s < 0) %(dst)s.data += %(offset)s;"
%
d
)
code
.
putln
(
"else %(dst)s.suboffsets[%(dim)s] += %(offset)s;"
%
d
)
else
:
# integer index (or object converted to integer index)
# Note: this dimension disappears
assert
access
==
'direct'
r
=
index
.
result
()
# Bounds checking with error
code
.
globalstate
.
use_utility_code
(
Buffer
.
raise_indexerror_code
)
out_of_bounds
=
"%s < 0 || %s >= %s"
%
(
r
,
r
,
shape
)
code
.
putln
(
"if (%s) {"
%
code
.
unlikely
(
out_of_bounds
))
code
.
putln
(
'__Pyx_RaiseBufferIndexError(%d);'
%
dim
)
code
.
putln
(
code
.
error_goto
(
index
.
pos
))
code
.
putln
(
"}"
)
if
all_direct
:
axes
.
append
([
dim
,
index
.
result
(),
access
,
packing
])
else
:
add_slice_offset
(
r
)
# Take care of data * for direct access in all dimensions
if
all_direct
:
bufp
=
self
.
_generate_buffer_lookup_code
(
code
,
axes
,
cast_result
=
False
)
code
.
putln
(
"%s.data = %s;"
%
(
dst
,
bufp
))
# Finally take care of memview
code
.
putln
(
"%s.memview = %s.memview;"
%
(
dst
,
self
.
cname
))
for
temp
in
temps
:
code
.
funcstate
.
release_temp
(
temp
)
def
unellipsify
(
indices
,
ndim
):
result
=
[]
seen_ellipsis
=
False
for
index
in
indices
:
if
isinstance
(
index
,
ExprNodes
.
EllipsisNode
):
none
=
ExprNodes
.
NoneNode
(
index
.
pos
)
full_slice
=
ExprNodes
.
SliceNode
(
index
.
pos
,
start
=
none
,
stop
=
none
,
step
=
none
)
if
seen_ellipsis
:
result
.
append
(
full_slice
)
else
:
nslices
=
ndim
-
len
(
indices
)
+
1
result
.
extend
([
full_slice
]
*
nslices
)
seen_ellipsis
=
True
else
:
result
.
append
(
index
)
return
result
def
get_memoryview_flag
(
access
,
packing
):
if
access
==
'full'
and
packing
in
(
'strided'
,
'follow'
):
...
...
@@ -250,7 +461,7 @@ def get_copy_contents_name(from_mvs, to_mvs):
class
IsContigFuncUtilCode
(
object
):
requires
=
None
def
__init__
(
self
,
c_or_f
):
...
...
@@ -337,7 +548,7 @@ static int %(copy_to_name)s(const __Pyx_memviewslice from_mvs, __Pyx_memviewslic
'''
class
CopyContentsFuncUtilCode
(
object
):
requires
=
None
def
__init__
(
self
,
from_memview
,
to_memview
):
...
...
@@ -587,7 +798,7 @@ def get_axes_specs(env, axes):
if
not
isinstance
(
axis
.
stop
,
NoneNode
):
raise
CompileError
(
axis
.
stop
.
pos
,
STOP_ERR
)
# step slot can be None, the value 1,
# step slot can be None, the value 1,
# a single axis spec, or an IntBinopNode.
if
isinstance
(
axis
.
step
,
NoneNode
):
if
is_c_contig
or
is_f_contig
:
...
...
@@ -598,7 +809,7 @@ def get_axes_specs(env, axes):
elif
isinstance
(
axis
.
step
,
IntNode
):
if
idx
not
in
(
0
,
len
(
axes
)
-
1
):
raise
CompileError
(
axis
.
step
.
pos
,
ONE_ERR
)
# the packing for the ::1 axis is contiguous,
# the packing for the ::1 axis is contiguous,
# all others are cf_packing.
axes_specs
.
append
((
cf_access
,
'contig'
))
...
...
@@ -636,7 +847,7 @@ def is_cf_contig(specs):
# c_contiguous: 'follow', 'follow', ..., 'follow', 'contig'
is_c_contig
=
True
elif
(
len
(
specs
)
>
1
and
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'
...
...
@@ -677,7 +888,7 @@ def validate_axes_specs(positions, specs):
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
for
pos
,
(
access
,
packing
)
in
zip
(
positions
,
specs
):
...
...
Cython/Compiler/Pipeline.py
View file @
155a4ef5
...
...
@@ -192,7 +192,6 @@ def create_pipeline(context, mode, exclude_classes=()):
FinalOptimizePhase
(
context
),
GilCheck
(),
UseUtilityCodeDefinitions
(
context
),
# PrintTree(),
]
filtered_stages
=
[]
for
s
in
stages
:
...
...
@@ -293,7 +292,9 @@ def insert_into_pipeline(pipeline, transform, before=None, after=None):
# Running a pipeline
#
def
run_pipeline
(
pipeline
,
source
):
def
run_pipeline
(
pipeline
,
source
,
printtree
=
True
):
from
Cython.Compiler.Visitor
import
PrintTree
error
=
None
data
=
source
try
:
...
...
@@ -303,6 +304,8 @@ def run_pipeline(pipeline, source):
if
DebugFlags
.
debug_verbose_pipeline
:
t
=
time
()
print
"Entering pipeline phase %r"
%
phase
if
not
printtree
and
isinstance
(
phase
,
PrintTree
):
continue
data
=
phase
(
data
)
if
DebugFlags
.
debug_verbose_pipeline
:
print
" %.3f seconds"
%
(
time
()
-
t
)
...
...
Cython/Compiler/PyrexTypes.py
View file @
155a4ef5
...
...
@@ -187,7 +187,7 @@ class PyrexType(BaseType):
def
needs_nonecheck
(
self
):
return
0
def
public_decl
(
base_code
,
dll_linkage
):
if
dll_linkage
:
return
"%s(%s)"
%
(
dll_linkage
,
base_code
)
...
...
@@ -414,8 +414,7 @@ class MemoryViewSliceType(PyrexType):
def
attributes_known
(
self
):
if
self
.
scope
is
None
:
import
Symtab
,
MemoryView
,
Options
import
Symtab
self
.
scope
=
scope
=
Symtab
.
CClassScope
(
'mvs_class_'
+
self
.
specialization_suffix
(),
...
...
@@ -424,8 +423,17 @@ class MemoryViewSliceType(PyrexType):
scope
.
parent_type
=
self
scope
.
declare_var
(
'_data'
,
c_char_ptr_type
,
None
,
cname
=
'data'
,
is_cdef
=
1
)
scope
.
declare_var
(
'_data'
,
c_char_ptr_type
,
None
,
cname
=
'data'
,
is_cdef
=
1
)
return
True
def
declare_attribute
(
self
,
attribute
):
import
MemoryView
,
Options
scope
=
self
.
scope
if
attribute
==
'shape'
:
scope
.
declare_var
(
'shape'
,
c_array_type
(
c_py_ssize_t_type
,
Options
.
buffer_max_dims
),
...
...
@@ -433,6 +441,7 @@ class MemoryViewSliceType(PyrexType):
cname
=
'shape'
,
is_cdef
=
1
)
elif
attribute
==
'strides'
:
scope
.
declare_var
(
'strides'
,
c_array_type
(
c_py_ssize_t_type
,
Options
.
buffer_max_dims
),
...
...
@@ -440,6 +449,7 @@ class MemoryViewSliceType(PyrexType):
cname
=
'strides'
,
is_cdef
=
1
)
elif
attribute
==
'suboffsets'
:
scope
.
declare_var
(
'suboffsets'
,
c_array_type
(
c_py_ssize_t_type
,
Options
.
buffer_max_dims
),
...
...
@@ -447,6 +457,7 @@ class MemoryViewSliceType(PyrexType):
cname
=
'suboffsets'
,
is_cdef
=
1
)
elif
attribute
in
(
"copy"
,
"copy_fortran"
):
ndim
=
len
(
self
.
axes
)
to_axes_c
=
[(
'direct'
,
'contig'
)]
...
...
Cython/Compiler/UtilityCode.py
View file @
155a4ef5
...
...
@@ -118,7 +118,7 @@ class CythonUtilityCode(Code.UtilityCodeBase):
pipeline
=
Pipeline
.
insert_into_pipeline
(
pipeline
,
scope_transform
,
before
=
transform
)
(
err
,
tree
)
=
Pipeline
.
run_pipeline
(
pipeline
,
tree
)
(
err
,
tree
)
=
Pipeline
.
run_pipeline
(
pipeline
,
tree
,
printtree
=
False
)
assert
not
err
,
err
return
tree
...
...
Cython/Utility/MemoryView_C.c
View file @
155a4ef5
...
...
@@ -5,7 +5,6 @@
typedef
struct
{
struct
{{
memview_struct_name
}}
*
memview
;
/* For convenience and faster access */
char
*
data
;
Py_ssize_t
shape
[{{
max_dims
}}];
Py_ssize_t
strides
[{{
max_dims
}}];
...
...
@@ -30,9 +29,6 @@ static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *);
#define __Pyx_IS_C_CONTIG 1
#define __Pyx_IS_F_CONTIG 2
/* #define __PYX_MEMSLICE_GETDATA(SLICE) ((char *) SLICE->memview->view->buf) */
static
int
__Pyx_ValidateAndInit_memviewslice
(
struct
__pyx_memoryview_obj
*
memview
,
int
*
axes_specs
,
int
c_or_f_flag
,
int
ndim
,
__Pyx_TypeInfo
*
dtype
,
__Pyx_BufFmt_StackElem
stack
[],
__Pyx_memviewslice
*
memviewslice
);
...
...
@@ -170,7 +166,7 @@ static int __Pyx_ValidateAndInit_memviewslice(
}
if
(
spec
&
__Pyx_MEMVIEW_PTR
)
{
if
(
buf
->
suboffsets
&&
buf
->
suboffsets
[
i
]
<
0
)
{
if
(
!
buf
->
suboffsets
||
(
buf
->
suboffsets
&&
buf
->
suboffsets
[
i
]
<
0
)
)
{
PyErr_Format
(
PyExc_ValueError
,
"Buffer is not indirectly accessisble in dimension %d."
,
i
);
goto
fail
;
...
...
@@ -287,7 +283,6 @@ static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice,
struct
{{
memview_struct_name
}}
*
memview
=
memslice
->
memview
;
if
(
!
memview
)
return
;
/* allow uninitialized memoryview assignment */
/* __pyx_fatalerror("memoryslice is not initialized (line %d)", lineno); */
if
(
memview
->
acquisition_count
<=
0
)
__pyx_fatalerror
(
"Acquisition count is %d (line %d)"
,
...
...
@@ -324,6 +319,7 @@ static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice,
last_time
=
(
memview
->
acquisition_count
--
==
1
);
PyThread_release_lock
(
memview
->
lock
);
memslice
->
data
=
NULL
;
if
(
last_time
)
{
if
(
have_gil
)
{
Py_CLEAR
(
memslice
->
memview
);
...
...
@@ -332,6 +328,7 @@ static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice,
Py_CLEAR
(
memslice
->
memview
);
PyGILState_Release
(
_gilstate
);
}
}
else
{
memslice
->
memview
=
NULL
;
}
}
...
...
@@ -466,4 +463,4 @@ int {{set_function}}(const char *itemp, PyObject *obj) {
Py_DECREF
(
*
(
PyObject
**
)
itemp
);
*
(
PyObject
**
)
itemp
=
obj
;
return
1
;
}
}
\ No newline at end of file
tests/run/cythonarray.pyx
View file @
155a4ef5
...
...
@@ -5,7 +5,8 @@ from __future__ import unicode_literals
from
cython
cimport
array
cimport
cython
as
cy
from
libc.stdlib
cimport
malloc
,
free
include
"cythonarrayutil.pxi"
def
contiguity
():
'''
...
...
@@ -67,26 +68,6 @@ def dont_allocate_buffer():
result
.
callback_free_data
=
callback
result
=
None
cdef
void
callback
(
char
*
data
):
print
"callback called %d"
%
<
long
>
data
cdef
create_array
(
shape
,
mode
):
cdef
array
result
=
array
(
shape
,
itemsize
=
sizeof
(
int
),
format
=
'i'
,
mode
=
mode
)
cdef
int
*
data
=
<
int
*>
result
.
data
cdef
int
i
,
j
,
cidx
,
fidx
for
i
in
range
(
shape
[
0
]):
for
j
in
range
(
shape
[
1
]):
cidx
=
i
*
shape
[
1
]
+
j
fidx
=
i
+
j
*
shape
[
0
]
if
mode
==
'fortran'
:
data
[
fidx
]
=
cidx
else
:
data
[
cidx
]
=
cidx
return
result
def
test_cython_array_getbuffer
():
"""
>>> test_cython_array_getbuffer()
...
...
tests/run/cythonarrayutil.pxi
0 → 100644
View file @
155a4ef5
from
libc.stdlib
cimport
malloc
,
free
cimport
cython
cdef
void
callback
(
char
*
data
):
print
"callback called %d"
%
<
long
>
data
def
create_array
(
shape
,
mode
,
use_callback
=
False
):
cdef
cython
.
array
result
=
cython
.
array
(
shape
,
itemsize
=
sizeof
(
int
),
format
=
'i'
,
mode
=
mode
)
cdef
int
*
data
=
<
int
*>
result
.
data
cdef
int
i
,
j
,
cidx
,
fidx
for
i
in
range
(
shape
[
0
]):
for
j
in
range
(
shape
[
1
]):
cidx
=
i
*
shape
[
1
]
+
j
fidx
=
i
+
j
*
shape
[
0
]
if
mode
==
'fortran'
:
data
[
fidx
]
=
cidx
else
:
data
[
cidx
]
=
cidx
if
use_callback
:
result
.
callback_free_data
=
callback
return
result
tests/run/memslice.pyx
View file @
155a4ef5
...
...
@@ -23,6 +23,7 @@ def testcase(func):
include
"mockbuffers.pxi"
include
"cythonarrayutil.pxi"
#
# Buffer acquire and release tests
...
...
@@ -1083,12 +1084,14 @@ def buffer_nogil():
#
### Test cdef functions
#
cdef
cdef
_function
(
int
[:]
buf1
,
object
[::
view
.
indirect
,
:]
buf2
=
ObjectMockBuffer
(
None
,
[[
"spam"
],[
"ham"
],[
"eggs"
]])):
objs
=
[[
"spam"
],
[
"ham"
],
[
"eggs"
]]
cdef
cdef
_function
(
int
[:]
buf1
,
object
[::
view
.
indirect
,
:]
buf2
=
ObjectMockBuffer
(
None
,
objs
)):
print
'cdef called'
print
buf1
[
6
],
buf2
[
1
,
0
]
buf2
[
1
,
0
]
=
"eggs"
@
testcase
def
test_cdef_function
(
o1
,
o2
=
None
):
"""
>>> A = IntMockBuffer("A", range(10))
...
...
@@ -1101,19 +1104,22 @@ def test_cdef_function(o1, o2=None):
cdef called
6 eggs
released A
>>> B = ObjectMockBuffer("B", range(25), shape=(5, 5))
>>> L = [[x] for x in range(25)]
>>> B = ObjectMockBuffer("B", L, shape=(5, 5))
>>> test_cdef_function(A, B)
acquired A
acquired B
cdef called
6 eggs
released A
released B
acquired A
acquired B
cdef called
6 eggs
released A
acquired A
acquired B
cdef called
6 1
released A
released B
"""
cdef
_function
(
o1
)
...
...
@@ -1122,15 +1128,15 @@ def test_cdef_function(o1, o2=None):
if
o2
:
cdef
_function
(
o1
,
o2
)
cdef
int
[:]
global_A
=
IntMockBuffer
(
"A"
,
range
(
10
))
cdef
object
[::
view
.
indirect
,
:]
global_B
=
ObjectMockBuffer
(
None
,
[[
"spam"
],[
"ham"
],[
"eggs"
]])
cdef
int
[:]
global_A
=
IntMockBuffer
(
"Global_A"
,
range
(
10
))
cdef
object
[::
view
.
indirect
,
:]
global_B
=
ObjectMockBuffer
(
None
,
objs
)
cdef
cdef
_function2
(
int
[:]
buf1
,
object
[::
view
.
indirect
,
:]
buf2
=
global_B
):
print
'cdef2 called'
print
buf1
[
6
],
buf2
[
1
,
0
]
buf2
[
1
,
0
]
=
"eggs"
@
testcase
def
test_cdef_function2
():
"""
>>> test_cdef_function2()
...
...
@@ -1151,3 +1157,76 @@ def test_cdef_function2():
print
global_B
[
1
,
0
]
cdef
_function2
(
global_A
,
global_B
)
@
testcase
def
test_slicing
(
arg
):
"""
Test simple slicing
>>> test_slicing(IntMockBuffer("A", range(8 * 14 * 11), shape=(8, 14, 11)))
acquired A
3 9 2
1232 -44 4
-1 -1 -1
released A
Test direct slicing, negative slice oob in dim 2
>>> test_slicing(IntMockBuffer("A", range(1 * 2 * 3), shape=(1, 2, 3)))
acquired A
0 0 2
48 -12 4
-1 -1 -1
released A
Test indirect slicing
>>> L = [[range(k * 12 + j * 4, k * 12 + j * 4 + 4) for j in xrange(3)] for k in xrange(5)]
>>> test_slicing(IntMockBuffer("A", L, shape=(5, 3, 4)))
acquired A
2 0 2
8 -4 4
0 0 -1
released A
"""
cdef
int
[::
view
.
generic
,
::
view
.
generic
,
:]
a
=
arg
cdef
int
[::
view
.
generic
,
::
view
.
generic
,
:]
b
=
a
[
2
:
8
:
2
,
-
4
:
1
:
-
1
,
1
:
3
]
print
b
.
shape
[
0
],
b
.
shape
[
1
],
b
.
shape
[
2
]
print
b
.
strides
[
0
],
b
.
strides
[
1
],
b
.
strides
[
2
]
print
b
.
suboffsets
[
0
],
b
.
suboffsets
[
1
],
b
.
suboffsets
[
2
]
cdef
int
i
,
j
,
k
for
i
in
range
(
b
.
shape
[
0
]):
for
j
in
range
(
b
.
shape
[
1
]):
for
k
in
range
(
b
.
shape
[
2
]):
itemA
=
a
[
2
+
2
*
i
,
-
4
-
j
,
1
+
k
]
itemB
=
b
[
i
,
j
,
k
]
assert
itemA
==
itemB
,
(
i
,
j
,
k
,
itemA
,
itemB
)
@
testcase
def
test_slicing_and_indexing
(
arg
):
"""
>>> a = IntStridedMockBuffer("A", range(10 * 3 * 5), shape=(10, 3, 5))
>>> test_slicing_and_indexing(a)
acquired A
5 2
60 8
126 113
[111]
released A
"""
cdef
int
[:,
:,
:]
a
=
arg
cdef
int
[:,
:]
b
=
a
[
-
5
:,
1
,
1
::
2
]
cdef
int
[:,
:]
c
=
b
[
4
:
1
:
-
1
,
::
-
1
]
cdef
int
[:]
d
=
c
[
2
,
1
:
2
]
print
b
.
shape
[
0
],
b
.
shape
[
1
]
print
b
.
strides
[
0
],
b
.
strides
[
1
]
cdef
int
i
,
j
for
i
in
range
(
b
.
shape
[
0
]):
for
j
in
range
(
b
.
shape
[
1
]):
itemA
=
a
[
-
5
+
i
,
1
,
1
+
2
*
j
]
itemB
=
b
[
i
,
j
]
assert
itemA
==
itemB
,
(
i
,
j
,
itemA
,
itemB
)
print
c
[
1
,
1
],
c
[
2
,
0
]
print
[
d
[
i
]
for
i
in
range
(
d
.
shape
[
0
])]
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment