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
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
nexedi
cython
Commits
1fc34d3b
Commit
1fc34d3b
authored
Nov 23, 2011
by
Mark Florisson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow casting C arrays to cython.arrays & direct assignment to memoryviews
parent
538fa670
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
126 additions
and
36 deletions
+126
-36
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+84
-24
docs/src/userguide/memoryviews.rst
docs/src/userguide/memoryviews.rst
+6
-2
tests/run/cythonarray.pyx
tests/run/cythonarray.pyx
+36
-10
No files found.
Cython/Compiler/ExprNodes.py
View file @
1fc34d3b
...
...
@@ -636,6 +636,9 @@ class ExprNode(Node):
if
not
src
.
type
.
is_memoryviewslice
:
if
src
.
type
.
is_pyobject
:
src
=
CoerceToMemViewSliceNode
(
src
,
dst_type
,
env
)
elif
src
.
type
.
is_array
:
src
=
CythonArrayNode
.
from_carray
(
src
,
env
).
coerce_to
(
dst_type
,
env
)
else
:
error
(
self
.
pos
,
"Cannot convert '%s' to memoryviewslice"
%
...
...
@@ -6763,7 +6766,7 @@ ERR_START = "Start may not be given"
ERR_NOT_STOP
=
"Stop must be provided to indicate shape"
ERR_STEPS
=
(
"Strides may only be given to indicate contiguity. "
"Consider slicing it after conversion"
)
ERR_NOT_POINTER
=
"Can only create cython.array from pointer"
ERR_NOT_POINTER
=
"Can only create cython.array from pointer
or array
"
ERR_BASE_TYPE
=
"Pointer base type does not match cython.array base type"
class
CythonArrayNode
(
ExprNode
):
...
...
@@ -6779,6 +6782,12 @@ class CythonArrayNode(ExprNode):
and less work. Acquiring a memoryviewslice from this will be just as
efficient. ExprNode.coerce_to() will do the additional typecheck on
self.compile_time_type
This also handles <int[:, :]> my_c_array
operand ExprNode the thing we're casting
base_type_node MemoryViewSliceTypeNode the cast expression node
"""
subexprs
=
[
'operand'
,
'shapes'
]
...
...
@@ -6786,21 +6795,62 @@ class CythonArrayNode(ExprNode):
shapes
=
None
is_temp
=
True
mode
=
"c"
array_dtype
=
None
shape_type
=
PyrexTypes
.
c_py_ssize_t_type
def
analyse_types
(
self
,
env
):
import
MemoryView
self
.
operand
.
analyse_types
(
env
)
if
self
.
array_dtype
:
array_dtype
=
self
.
array_dtype
else
:
array_dtype
=
self
.
base_type_node
.
base_type_node
.
analyse
(
env
)
axes
=
self
.
base_type_node
.
axes
MemoryView
.
validate_memslice_dtype
(
self
.
pos
,
array_dtype
)
self
.
type
=
error_type
self
.
shapes
=
[]
ndim
=
len
(
axes
)
for
axis_no
,
axis
in
enumerate
(
self
.
base_type_node
.
axes
):
# Base type of the pointer or C array we are converting
base_type
=
self
.
operand
.
type
# Dimension sizes of C array
array_dimension_sizes
=
[]
if
base_type
.
is_array
:
while
base_type
.
is_array
:
array_dimension_sizes
.
append
(
base_type
.
size
)
base_type
=
base_type
.
base_type
else
:
base_type
=
base_type
.
base_type
if
not
self
.
operand
.
type
.
is_ptr
and
not
self
.
operand
.
type
.
is_array
:
return
error
(
self
.
operand
.
pos
,
ERR_NOT_POINTER
)
elif
not
base_type
.
same_as
(
array_dtype
):
return
error
(
self
.
operand
.
pos
,
ERR_BASE_TYPE
)
elif
self
.
operand
.
type
.
is_array
and
len
(
array_dimension_sizes
)
!=
ndim
:
return
error
(
self
.
operand
.
pos
,
"Expected %d dimensions, array has %d dimensions"
%
(
ndim
,
len
(
array_dimension_sizes
)))
# Verify the start, stop and step values
# In case of a C array, use the size of C array in each dimension to
# get an automatic cast
for
axis_no
,
axis
in
enumerate
(
axes
):
if
not
axis
.
start
.
is_none
:
return
error
(
axis
.
start
.
pos
,
ERR_START
)
if
axis
.
stop
.
is_none
:
return
error
(
axis
.
pos
,
ERR_NOT_STOP
)
if
array_dimension_sizes
:
dimsize
=
array_dimension_sizes
[
axis_no
]
axis
.
stop
=
IntNode
(
self
.
pos
,
value
=
dimsize
,
constant_result
=
dimsize
,
type
=
PyrexTypes
.
c_int_type
)
else
:
return
error
(
axis
.
pos
,
ERR_NOT_STOP
)
axis
.
stop
.
analyse_types
(
env
)
shape
=
axis
.
stop
.
coerce_to
(
self
.
shape_type
,
env
)
...
...
@@ -6812,7 +6862,7 @@ class CythonArrayNode(ExprNode):
if
not
axis
.
stop
.
type
.
is_int
:
return
error
(
axis
.
stop
.
pos
,
"Expected an integer type"
)
first_or_last
=
axis_no
in
(
0
,
len
(
self
.
base_type_node
.
axes
)
-
1
)
first_or_last
=
axis_no
in
(
0
,
ndim
-
1
)
if
not
axis
.
step
.
is_none
and
first_or_last
:
axis
.
step
.
analyse_types
(
env
)
if
(
not
axis
.
step
.
type
.
is_int
and
axis
.
step
.
is_literal
and
not
...
...
@@ -6828,31 +6878,17 @@ class CythonArrayNode(ExprNode):
elif
axis
.
step
and
not
first_or_last
:
return
error
(
axis
.
step
.
pos
,
ERR_STEPS
)
self
.
operand
.
analyse_types
(
env
)
array_dtype
=
self
.
base_type_node
.
base_type_node
.
analyse
(
env
)
MemoryView
.
validate_memslice_dtype
(
self
.
pos
,
array_dtype
)
if
not
self
.
operand
.
type
.
is_ptr
:
return
error
(
self
.
operand
.
pos
,
ERR_NOT_POINTER
)
elif
not
self
.
operand
.
type
.
base_type
.
same_as
(
array_dtype
):
return
error
(
self
.
operand
.
pos
,
ERR_BASE_TYPE
)
if
not
self
.
operand
.
is_name
:
self
.
operand
=
self
.
operand
.
coerce_to_temp
(
env
)
axes
=
[(
'direct'
,
'follow'
)]
*
len
(
self
.
base_type_node
.
axes
)
axes
=
[(
'direct'
,
'follow'
)]
*
len
(
axes
)
if
self
.
mode
==
"fortran"
:
axes
[
0
]
=
(
'direct'
,
'contig'
)
else
:
axes
[
-
1
]
=
(
'direct'
,
'contig'
)
self
.
coercion_type
=
PyrexTypes
.
MemoryViewSliceType
(
array_dtype
,
axes
)
#self.type = py_object_type
self
.
type
=
self
.
get_cython_array_type
(
env
)
assert
self
.
type
MemoryView
.
use_cython_array_utility_code
(
env
)
env
.
use_utility_code
(
MemoryView
.
typeinfo_to_format_code
)
...
...
@@ -6881,11 +6917,12 @@ class CythonArrayNode(ExprNode):
itemsize
=
"sizeof(%s)"
%
dtype
.
declaration_code
(
""
)
type_info
=
Buffer
.
get_type_information_cname
(
code
,
dtype
)
code
.
putln
(
"if (!%s) {"
%
self
.
operand
.
result
())
code
.
putln
(
'PyErr_SetString(PyExc_ValueError,'
'"Cannot create cython.array from NULL pointer");'
)
code
.
putln
(
code
.
error_goto
(
self
.
operand
.
pos
))
code
.
putln
(
"}"
)
if
self
.
operand
.
type
.
is_ptr
:
code
.
putln
(
"if (!%s) {"
%
self
.
operand
.
result
())
code
.
putln
(
'PyErr_SetString(PyExc_ValueError,'
'"Cannot create cython.array from NULL pointer");'
)
code
.
putln
(
code
.
error_goto
(
self
.
operand
.
pos
))
code
.
putln
(
"}"
)
code
.
putln
(
"%s = __pyx_format_from_typeinfo(&%s);"
%
(
format_temp
,
type_info
))
...
...
@@ -6914,6 +6951,29 @@ class CythonArrayNode(ExprNode):
dispose
(
shapes_temp
)
dispose
(
format_temp
)
@
classmethod
def
from_carray
(
cls
,
src_node
,
env
):
"""
Given a C array type, return a CythonArrayNode
"""
pos
=
src_node
.
pos
base_type
=
src_node
.
type
none_node
=
NoneNode
(
pos
)
axes
=
[]
while
base_type
.
is_array
:
axes
.
append
(
SliceNode
(
pos
,
start
=
none_node
,
stop
=
none_node
,
step
=
none_node
))
base_type
=
base_type
.
base_type
axes
[
-
1
].
step
=
IntNode
(
pos
,
value
=
"1"
,
is_c_literal
=
True
)
memslicenode
=
Nodes
.
MemoryViewSliceTypeNode
(
pos
,
axes
=
axes
,
base_type_node
=
base_type
)
result
=
CythonArrayNode
(
pos
,
base_type_node
=
memslicenode
,
operand
=
src_node
,
array_dtype
=
base_type
)
result
.
analyse_types
(
env
)
return
result
class
SizeofNode
(
ExprNode
):
# Abstract base class for sizeof(x) expression nodes.
...
...
docs/src/userguide/memoryviews.rst
View file @
1fc34d3b
...
...
@@ -193,11 +193,15 @@ It also takes an optional argument `mode` ('c' or 'fortran') and a boolean `allo
# define a function that can deallocate the data (if needed)
my_array.callback_free_data = free
You can also cast pointers to arrays::
You can also cast pointers to array
, or C arrays to array
s::
cdef cython.array my_array = <int[:10, :2]> my_data_pointer
cdef cython.array my_array = <int[:, :]> my_c_array
Of course, you can also immidiately assign a cython.array to a typed memoryview slice.
Of course, you can also immediately assign a cython.array to a typed memoryview slice. A C array
may be assigned directly to a memoryview slice::
cdef int[:, ::1] myslice = my_2d_c_array
The arrays are indexable and slicable from Python space just like memoryview objects, and have the same
attributes as memoryview objects.
...
...
tests/run/cythonarray.pyx
View file @
1fc34d3b
...
...
@@ -64,7 +64,6 @@ def dont_allocate_buffer():
"""
cdef
cy
.
array
result
=
cy
.
array
((
10
,
10
),
itemsize
=
sizeof
(
int
),
format
=
'i'
,
allocate_buffer
=
False
)
assert
result
.
data
==
NULL
result
.
data
=
<
char
*>
1
result
.
callback_free_data
=
callback
result
=
None
...
...
@@ -115,7 +114,7 @@ cdef int *getp(int dim1=10, int dim2=10) except NULL:
return
p
cdef
void
callback_free_data
(
char
*
p
):
cdef
void
callback_free_data
(
void
*
p
):
print
'callback free data called'
free
(
p
)
...
...
@@ -126,11 +125,14 @@ def test_array_from_pointer():
69
c
getp()
callback free data called
fortran
getp()
56
getp()
56
getp()
119
callback free data called
"""
cdef
int
*
p
=
getp
()
...
...
@@ -139,14 +141,38 @@ def test_array_from_pointer():
print
c_arr
[
6
,
9
]
print
c_arr
.
mode
print
(
<
int
[:
10
:
1
,
:
10
]
>
getp
()).
mode
c_arr
=
(
<
int
[:
10
:
1
,
:
10
]
>
getp
())
print
c_arr
.
mode
c_arr
.
callback_free_data
=
free
cdef
int
[:,
::
1
]
mslice
=
<
int
[:
10
,
:
10
]
>
getp
()
c_arr
=
<
int
[:
10
,
:
10
]
>
getp
()
c_arr
.
callback_free_data
=
free
cdef
int
[:,
::
1
]
mslice
=
c_arr
print
mslice
[
5
,
6
]
print
(
<
int
[:
12
,
:
10
]
>
getp
(
12
,
10
))[
5
,
6
]
# There is a reference cycle between the array object to its memoryview
# object that it keeps
del
c_arr
import
gc
gc
.
collect
()
c_arr
=
<
int
[:
12
,
:
10
]
>
getp
(
12
,
10
)
c_arr
.
callback_free_data
=
free
print
c_arr
[
5
,
6
]
cdef
int
m
=
12
cdef
int
n
=
10
c_arr
=
<
int
[:
m
,
:
n
]
>
getp
(
m
,
n
)
c_arr
.
callback_free_data
=
callback_free_data
print
c_arr
[
m
-
1
,
n
-
1
]
def
test_cyarray_from_carray
():
"""
>>> test_cyarray_from_carray()
0 8 21
0 8 21
"""
cdef
int
a
[
7
][
8
]
for
i
in
range
(
7
):
for
j
in
range
(
8
):
a
[
i
][
j
]
=
i
*
8
+
j
cdef
int
[:,
:]
mslice
=
<
int
[:,
:]
>
a
print
mslice
[
0
,
0
],
mslice
[
1
,
0
],
mslice
[
2
,
5
]
mslice
=
a
print
mslice
[
0
,
0
],
mslice
[
1
,
0
],
mslice
[
2
,
5
]
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