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
db608ca8
Commit
db608ca8
authored
Sep 17, 2008
by
Dag Sverre Seljebotn
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Buffers: Added C and Fortran contiguous modes
parent
e31b14ad
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
174 additions
and
31 deletions
+174
-31
Cython/Compiler/Buffer.py
Cython/Compiler/Buffer.py
+71
-25
Cython/Includes/numpy.pxd
Cython/Includes/numpy.pxd
+20
-5
tests/run/bufaccess.pyx
tests/run/bufaccess.pyx
+50
-1
tests/run/numpy_test.pyx
tests/run/numpy_test.pyx
+33
-0
No files found.
Cython/Compiler/Buffer.py
View file @
db608ca8
...
...
@@ -92,7 +92,7 @@ class IntroduceBufferAuxiliaryVars(CythonTransform):
mode
=
entry
.
type
.
mode
if
mode
==
'full'
:
suboffsetvars
=
[
var
(
Naming
.
bufsuboffset_prefix
,
i
,
"-1"
)
for
i
in
range
(
entry
.
type
.
ndim
)]
el
if
mode
==
'strided'
:
el
se
:
suboffsetvars
=
None
entry
.
buffer_aux
=
Symtab
.
BufferAux
(
bufinfo
,
stridevars
,
shapevars
,
suboffsetvars
)
...
...
@@ -121,7 +121,7 @@ ERR_BUF_OPTION_UNKNOWN = '"%s" is not a buffer option'
ERR_BUF_TOO_MANY
=
'Too many buffer options'
ERR_BUF_DUP
=
'"%s" buffer option already supplied'
ERR_BUF_MISSING
=
'"%s" missing'
ERR_BUF_MODE
=
'Only allowed buffer modes are
"full" or
"strided" (as a compile-time string)'
ERR_BUF_MODE
=
'Only allowed buffer modes are
: "c", "fortran", "full",
"strided" (as a compile-time string)'
ERR_BUF_NDIM
=
'ndim must be a non-negative integer'
ERR_BUF_DTYPE
=
'dtype must be "object", numeric type or a struct'
...
...
@@ -175,7 +175,7 @@ def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, nee
raise
CompileError
(
globalpos
,
ERR_BUF_NDIM
)
mode
=
options
.
get
(
"mode"
)
if
mode
and
not
(
mode
in
(
'full'
,
'strided'
)):
if
mode
and
not
(
mode
in
(
'full'
,
'strided'
,
'c'
,
'fortran'
)):
raise
CompileError
(
globalpos
,
ERR_BUF_MODE
)
return
options
...
...
@@ -188,10 +188,15 @@ def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, nee
def
get_flags
(
buffer_aux
,
buffer_type
):
flags
=
'PyBUF_FORMAT'
if
buffer_type
.
mode
==
'full'
:
mode
=
buffer_type
.
mode
if
mode
==
'full'
:
flags
+=
'| PyBUF_INDIRECT'
elif
buffer_type
.
mode
==
'strided'
:
elif
mode
==
'strided'
:
flags
+=
'| PyBUF_STRIDES'
elif
mode
==
'c'
:
flags
+=
'| PyBUF_C_CONTIGUOUS'
elif
mode
==
'fortran'
:
flags
+=
'| PyBUF_F_CONTIGUOUS'
else
:
assert
False
if
buffer_aux
.
writable_needed
:
flags
+=
"| PyBUF_WRITABLE"
...
...
@@ -367,28 +372,43 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod
code
.
putln
(
"if (%s < 0) %s += %s;"
%
(
cname
,
cname
,
shape
.
cname
))
# Create buffer lookup and return it
# This is done via utility macros/inline functions, which vary
# according to the access mode used.
params
=
[]
nd
=
entry
.
type
.
ndim
if
entry
.
type
.
mode
==
'full'
:
mode
=
entry
.
type
.
mode
if
mode
==
'full'
:
for
i
,
s
,
o
in
zip
(
index_cnames
,
bufaux
.
stridevars
,
bufaux
.
suboffsetvars
):
params
.
append
(
i
)
params
.
append
(
s
.
cname
)
params
.
append
(
o
.
cname
)
funcname
=
"__Pyx_BufPtrFull%dd"
%
nd
funcgen
=
buf_lookup_full_code
else
:
if
mode
==
'strided'
:
funcname
=
"__Pyx_BufPtrStrided%dd"
%
nd
funcgen
=
buf_lookup_strided_code
elif
mode
==
'c'
:
funcname
=
"__Pyx_BufPtrCContig%dd"
%
nd
funcgen
=
buf_lookup_c_code
elif
mode
==
'fortran'
:
funcname
=
"__Pyx_BufPtrFortranContig%dd"
%
nd
funcgen
=
buf_lookup_fortran_code
else
:
assert
False
for
i
,
s
in
zip
(
index_cnames
,
bufaux
.
stridevars
):
params
.
append
(
i
)
params
.
append
(
s
.
cname
)
funcname
=
"__Pyx_BufPtrStrided%dd"
%
nd
funcgen
=
buf_lookup_strided_code
# Make sure the utility code is available
code
.
globalstate
.
use_generated_code
(
funcgen
,
name
=
funcname
,
nd
=
nd
)
ptrcode
=
"%s(%s.buf, %s)"
%
(
funcname
,
bufstruct
,
", "
.
join
(
params
))
return
entry
.
type
.
buffer_ptr_type
.
cast_code
(
ptrcode
)
ptr_type
=
entry
.
type
.
buffer_ptr_type
ptrcode
=
"%s(%s, %s.buf, %s)"
%
(
funcname
,
ptr_type
.
declaration_code
(
""
),
bufstruct
,
", "
.
join
(
params
))
return
ptrcode
def
use_empty_bufstruct_code
(
env
,
max_ndim
):
...
...
@@ -399,33 +419,59 @@ def use_empty_bufstruct_code(env, max_ndim):
env
.
use_utility_code
([
code
,
""
],
"empty_bufstruct_code"
)
def
buf_lookup_strided_code
(
proto
,
defin
,
name
,
nd
):
"""
Generates a buffer lookup function for the right number
of dimensions. The function gives back a void* at the right location.
"""
# _i_ndex, _s_tride
args
=
", "
.
join
([
"i%d, s%d"
%
(
i
,
i
)
for
i
in
range
(
nd
)])
offset
=
" + "
.
join
([
"i%d * s%d"
%
(
i
,
i
)
for
i
in
range
(
nd
)])
proto
.
putln
(
"#define %s(buf, %s) ((char*)buf + %s)"
%
(
name
,
args
,
offset
))
def
buf_lookup_full_code
(
proto
,
defin
,
name
,
nd
):
"""
Generates a buffer lookup function for the right number
of dimensions. The function gives back a void* at the right location.
"""
# _i_ndex, _s_tride, sub_o_ffset
args
=
", "
.
join
([
"Py_ssize_t i%d, Py_ssize_t s%d, Py_ssize_t o%d"
%
(
i
,
i
,
i
)
for
i
in
range
(
nd
)])
proto
.
putln
(
"static INLINE void* %s(void* buf, %s);"
%
(
name
,
args
))
macroargs
=
", "
.
join
([
"i%d, s%d, o%d"
%
(
i
,
i
,
i
)
for
i
in
range
(
nd
)])
proto
.
putln
(
"#define %s(type, buf, %s) (type)(%s_imp(buf, %s))"
%
(
name
,
macroargs
,
name
,
macroargs
))
funcargs
=
", "
.
join
([
"Py_ssize_t i%d, Py_ssize_t s%d, Py_ssize_t o%d"
%
(
i
,
i
,
i
)
for
i
in
range
(
nd
)])
proto
.
putln
(
"static INLINE void* %s_imp(void* buf, %s);"
%
(
name
,
funcargs
))
defin
.
putln
(
dedent
(
"""
static INLINE void* %s(void* buf, %s) {
static INLINE void* %s
_imp
(void* buf, %s) {
char* ptr = (char*)buf;
"""
)
%
(
name
,
args
)
+
""
.
join
([
dedent
(
"""
\
"""
)
%
(
name
,
func
args
)
+
""
.
join
([
dedent
(
"""
\
ptr += s%d * i%d;
if (o%d >= 0) ptr = *((char**)ptr) + o%d;
"""
)
%
(
i
,
i
,
i
,
i
)
for
i
in
range
(
nd
)]
)
+
"
\
n
return ptr;
\
n
}"
)
def
buf_lookup_strided_code
(
proto
,
defin
,
name
,
nd
):
"""
Generates a buffer lookup function for the right number
of dimensions. The function gives back a void* at the right location.
"""
# _i_ndex, _s_tride
args
=
", "
.
join
([
"i%d, s%d"
%
(
i
,
i
)
for
i
in
range
(
nd
)])
offset
=
" + "
.
join
([
"i%d * s%d"
%
(
i
,
i
)
for
i
in
range
(
nd
)])
proto
.
putln
(
"#define %s(type, buf, %s) (type)((char*)buf + %s)"
%
(
name
,
args
,
offset
))
def
buf_lookup_c_code
(
proto
,
defin
,
name
,
nd
):
"""
Similar to strided lookup, but can assume that the last dimension
doesn't need a multiplication as long as.
Still we keep the same signature for now.
"""
if
nd
==
1
:
proto
.
putln
(
"#define %s(type, buf, i0, s0) ((type)buf + i0)"
%
name
)
else
:
args
=
", "
.
join
([
"i%d, s%d"
%
(
i
,
i
)
for
i
in
range
(
nd
)])
offset
=
" + "
.
join
([
"i%d * s%d"
%
(
i
,
i
)
for
i
in
range
(
nd
-
1
)])
proto
.
putln
(
"#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)"
%
(
name
,
args
,
offset
,
nd
-
1
))
def
buf_lookup_fortran_code
(
proto
,
defin
,
name
,
nd
):
"""
Like C lookup, but the first index is optimized instead.
"""
if
nd
==
1
:
proto
.
putln
(
"#define %s(type, buf, i0, s0) ((type)buf + i0)"
%
name
)
else
:
args
=
", "
.
join
([
"i%d, s%d"
%
(
i
,
i
)
for
i
in
range
(
nd
)])
offset
=
" + "
.
join
([
"i%d * s%d"
%
(
i
,
i
)
for
i
in
range
(
1
,
nd
)])
proto
.
putln
(
"#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)"
%
(
name
,
args
,
offset
,
0
))
#
# Utils for creating type string checkers
...
...
Cython/Includes/numpy.pxd
View file @
db608ca8
cimport
python_buffer
as
pybuf
cdef
extern
from
"Python.h"
:
ctypedef
int
Py_intptr_t
...
...
@@ -19,7 +21,11 @@ cdef extern from "numpy/arrayobject.h":
NPY_NTYPES
,
NPY_NOTYPE
,
NPY_CHAR
,
NPY_USERDEF
NPY_USERDEF
,
NPY_C_CONTIGUOUS
,
NPY_F_CONTIGUOUS
ctypedef
class
numpy
.
ndarray
[
object
PyArrayObject
]:
cdef
__cythonbufferdefaults__
=
{
"mode"
:
"strided"
}
...
...
@@ -29,19 +35,27 @@ cdef extern from "numpy/arrayobject.h":
int
ndim
"nd"
npy_intp
*
shape
"dimensions"
npy_intp
*
strides
int
flags
# Note: This syntax (function definition in pxd files) is an
# experimental exception made for __getbuffer__ and __releasebuffer__
# -- the details of this may change.
def
__getbuffer__
(
ndarray
self
,
Py_buffer
*
info
,
int
flags
):
# This implementation of getbuffer is geared towards Cython
# requirements, and does not yet fullfill the PEP (specifically,
# Cython always requests and we always provide strided access,
# so the flags are not even checked).
# requirements, and does not yet fullfill the PEP.
# In particular strided access is always provided regardless
# of flags
if
sizeof
(
npy_intp
)
!=
sizeof
(
Py_ssize_t
):
raise
RuntimeError
(
"Py_intptr_t and Py_ssize_t differs in size, numpy.pxd does not support this"
)
if
((
flags
&
pybuf
.
PyBUF_C_CONTIGUOUS
==
pybuf
.
PyBUF_C_CONTIGUOUS
)
and
not
PyArray_CHKFLAGS
(
self
,
NPY_C_CONTIGUOUS
)):
raise
ValueError
(
"ndarray is not C contiguous"
)
if
((
flags
&
pybuf
.
PyBUF_F_CONTIGUOUS
==
pybuf
.
PyBUF_F_CONTIGUOUS
)
and
not
PyArray_CHKFLAGS
(
self
,
NPY_F_CONTIGUOUS
)):
raise
ValueError
(
"ndarray is not Fortran contiguous"
)
info
.
buf
=
PyArray_DATA
(
self
)
# info.obj = None # this is automatic
info
.
ndim
=
PyArray_NDIM
(
self
)
...
...
@@ -82,6 +96,7 @@ cdef extern from "numpy/arrayobject.h":
cdef
npy_intp
PyArray_STRIDES
(
ndarray
arr
)
cdef
npy_intp
PyArray_DIMS
(
ndarray
arr
)
cdef
Py_ssize_t
PyArray_ITEMSIZE
(
ndarray
arr
)
cdef
int
PyArray_CHKFLAGS
(
ndarray
arr
,
int
flags
)
ctypedef
signed
int
npy_byte
ctypedef
signed
int
npy_short
...
...
tests/run/bufaccess.pyx
View file @
db608ca8
...
...
@@ -553,6 +553,54 @@ def strided(object[int, ndim=1, mode='strided'] buf):
"""
return
buf
[
2
]
@
testcase
def
c_contig
(
object
[
int
,
ndim
=
1
,
mode
=
'c'
]
buf
):
"""
>>> A = IntMockBuffer(None, range(4))
>>> c_contig(A)
2
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS']
"""
return
buf
[
2
]
@
testcase
def
c_contig_2d
(
object
[
int
,
ndim
=
2
,
mode
=
'c'
]
buf
):
"""
Multi-dim has seperate implementation
>>> A = IntMockBuffer(None, range(12), shape=(3,4))
>>> c_contig_2d(A)
7
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS']
"""
return
buf
[
1
,
3
]
@
testcase
def
f_contig
(
object
[
int
,
ndim
=
1
,
mode
=
'fortran'
]
buf
):
"""
>>> A = IntMockBuffer(None, range(4))
>>> f_contig(A)
2
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS']
"""
return
buf
[
2
]
@
testcase
def
f_contig_2d
(
object
[
int
,
ndim
=
2
,
mode
=
'fortran'
]
buf
):
"""
Must set up strides manually to ensure Fortran ordering.
>>> A = IntMockBuffer(None, range(12), shape=(4,3), strides=(1, 4))
>>> f_contig_2d(A)
7
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS']
"""
return
buf
[
3
,
1
]
#
# Test compiler options for bounds checking. We create an array with a
# safe "boundary" (memory
...
...
@@ -877,6 +925,8 @@ available_flags = (
(
'INDIRECT'
,
python_buffer
.
PyBUF_INDIRECT
),
(
'ND'
,
python_buffer
.
PyBUF_ND
),
(
'STRIDES'
,
python_buffer
.
PyBUF_STRIDES
),
(
'C_CONTIGUOUS'
,
python_buffer
.
PyBUF_C_CONTIGUOUS
),
(
'F_CONTIGUOUS'
,
python_buffer
.
PyBUF_F_CONTIGUOUS
),
(
'WRITABLE'
,
python_buffer
.
PyBUF_WRITABLE
)
)
...
...
@@ -913,7 +963,6 @@ cdef class MockBuffer:
strides
.
reverse
()
strides
=
[
x
*
self
.
itemsize
for
x
in
strides
]
suboffsets
=
[
-
1
]
*
len
(
shape
)
datashape
=
[
len
(
data
)]
p
=
data
while
True
:
...
...
tests/run/numpy_test.pyx
View file @
db608ca8
...
...
@@ -78,6 +78,30 @@ try:
>>> print a
[[0 0 0 0 0]
[0 0 0 0 0]]
Test contiguous access modes:
>>> c_arr = np.array(np.arange(12, dtype='i').reshape(3,4), order='C')
>>> f_arr = np.array(np.arange(12, dtype='i').reshape(3,4), order='F')
>>> test_c_contig(c_arr)
0 1 2 3
4 5 6 7
8 9 10 11
>>> test_f_contig(f_arr)
0 1 2 3
4 5 6 7
8 9 10 11
>>> test_c_contig(f_arr)
Traceback (most recent call last):
...
ValueError: ndarray is not C contiguous
>>> test_f_contig(c_arr)
Traceback (most recent call last):
...
ValueError: ndarray is not Fortran contiguous
>>> test_c_contig(c_arr[::2,::2])
Traceback (most recent call last):
...
ValueError: ndarray is not C contiguous
>>> test_dtype('b', inc1_byte)
>>> test_dtype('B', inc1_ubyte)
...
...
@@ -153,6 +177,15 @@ def put_range_long_1d(np.ndarray[long] arr):
arr
[
i
]
=
value
value
+=
1
def
test_c_contig
(
np
.
ndarray
[
int
,
ndim
=
2
,
mode
=
'c'
]
arr
):
cdef
int
i
,
j
for
i
in
range
(
arr
.
shape
[
0
]):
print
" "
.
join
([
str
(
arr
[
i
,
j
])
for
j
in
range
(
arr
.
shape
[
1
])])
def
test_f_contig
(
np
.
ndarray
[
int
,
ndim
=
2
,
mode
=
'fortran'
]
arr
):
cdef
int
i
,
j
for
i
in
range
(
arr
.
shape
[
0
]):
print
" "
.
join
([
str
(
arr
[
i
,
j
])
for
j
in
range
(
arr
.
shape
[
1
])])
# Exhaustive dtype tests -- increments element [1] by 1 for all dtypes
def
inc1_byte
(
np
.
ndarray
[
char
]
arr
):
arr
[
1
]
+=
1
...
...
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