Commit 155a4ef5 authored by Mark Florisson's avatar Mark Florisson

Support slicing memoryview slices

parent 917cce5e
......@@ -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 (is_memslice and not self.indices and
isinstance(self.index, EllipsisNode)):
memoryviewslice_access = True
elif self.base.type.is_buffer or is_memslice:
if self.indices:
indices = self.indices
else:
if isinstance(self.index, TupleNode):
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 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:
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
......
......@@ -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,8 +223,215 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
bufp = '( /* dim=%d */ %s )' % (dim, 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'):
return 'generic'
......
......@@ -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)
......
......@@ -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')]
......
......@@ -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
......
......@@ -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;
}
}
......
......@@ -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()
......
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
......@@ -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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment