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

Support slicing memoryview slices

parent 917cce5e
...@@ -2266,6 +2266,10 @@ class IndexNode(ExprNode): ...@@ -2266,6 +2266,10 @@ class IndexNode(ExprNode):
# writable) # writable)
writable_needed = False writable_needed = False
# Whether we are indexing or slicing a memoryviewslice
memslice_index = False
memslice_slice = False
def __init__(self, pos, index, *args, **kw): def __init__(self, pos, index, *args, **kw):
ExprNode.__init__(self, pos, index=index, *args, **kw) ExprNode.__init__(self, pos, index=index, *args, **kw)
self._index = index self._index = index
...@@ -2286,8 +2290,11 @@ class IndexNode(ExprNode): ...@@ -2286,8 +2290,11 @@ class IndexNode(ExprNode):
return self.base.is_ephemeral() return self.base.is_ephemeral()
def is_simple(self): def is_simple(self):
if self.is_buffer_access: if self.is_buffer_access or self.memslice_index:
return False return False
elif self.memslice_slice:
return True
base = self.base base = self.base
return (base.is_simple() and self.index.is_simple() return (base.is_simple() and self.index.is_simple()
and base.type and (base.type.is_ptr or base.type.is_array)) and base.type and (base.type.is_ptr or base.type.is_array))
...@@ -2377,7 +2384,13 @@ class IndexNode(ExprNode): ...@@ -2377,7 +2384,13 @@ class IndexNode(ExprNode):
# For buffers, self.index is packed out on the initial analysis, and # For buffers, self.index is packed out on the initial analysis, and
# when cloning self.indices is copied. # when cloning self.indices is copied.
self.is_buffer_access = False self.is_buffer_access = False
# a[...] = b
self.is_memoryviewslice_access = False 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) self.base.analyse_types(env)
if self.base.type.is_error: if self.base.type.is_error:
...@@ -2402,18 +2415,84 @@ class IndexNode(ExprNode): ...@@ -2402,18 +2415,84 @@ class IndexNode(ExprNode):
is_memslice = self.base.type.is_memoryviewslice 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 if (is_memslice and not self.indices and
isinstance(self.index, EllipsisNode)): isinstance(self.index, EllipsisNode)):
# Memoryviewslice copying
memoryviewslice_access = True memoryviewslice_access = True
elif self.base.type.is_buffer or is_memslice:
if self.indices: elif is_memslice:
indices = self.indices # memoryviewslice indexing or slicing
else: import MemoryView
if isinstance(self.index, TupleNode):
indices = self.index.args 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: 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: if len(indices) == self.base.type.ndim:
buffer_access = True buffer_access = True
skip_child_analysis = True skip_child_analysis = True
...@@ -2430,7 +2509,7 @@ class IndexNode(ExprNode): ...@@ -2430,7 +2509,7 @@ class IndexNode(ExprNode):
self.nogil = env.nogil 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: if self.base.type.is_memoryviewslice and not self.base.is_name:
self.base = self.base.coerce_to_temp(env) self.base = self.base.coerce_to_temp(env)
...@@ -2459,6 +2538,11 @@ class IndexNode(ExprNode): ...@@ -2459,6 +2538,11 @@ class IndexNode(ExprNode):
if getting: if getting:
error(self.pos, "memoryviews currently support setting only.") 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: else:
base_type = self.base.type base_type = self.base.type
if isinstance(self.index, TupleNode): if isinstance(self.index, TupleNode):
...@@ -2533,7 +2617,7 @@ class IndexNode(ExprNode): ...@@ -2533,7 +2617,7 @@ class IndexNode(ExprNode):
gil_message = "Indexing Python object" gil_message = "Indexing Python object"
def nogil_check(self, env): def nogil_check(self, env):
if self.is_buffer_access: if self.is_buffer_access or self.memslice_index:
if env.directives['boundscheck']: if env.directives['boundscheck']:
error(self.pos, "Cannot check buffer index bounds without gil; use boundscheck(False) directive") error(self.pos, "Cannot check buffer index bounds without gil; use boundscheck(False) directive")
return return
...@@ -2599,7 +2683,7 @@ class IndexNode(ExprNode): ...@@ -2599,7 +2683,7 @@ class IndexNode(ExprNode):
i.free_temps(code) i.free_temps(code)
def generate_result_code(self, 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']: if code.globalstate.directives['nonecheck']:
self.put_nonecheck(code) self.put_nonecheck(code)
buffer_entry, self.buffer_ptr_code = self.buffer_lookup_code(code) buffer_entry, self.buffer_ptr_code = self.buffer_lookup_code(code)
...@@ -2607,6 +2691,10 @@ class IndexNode(ExprNode): ...@@ -2607,6 +2691,10 @@ class IndexNode(ExprNode):
# is_temp is True, so must pull out value and incref it. # is_temp is True, so must pull out value and incref it.
code.putln("%s = *%s;" % (self.result(), self.buffer_ptr_code)) code.putln("%s = *%s;" % (self.result(), self.buffer_ptr_code))
code.putln("__Pyx_INCREF((PyObject*)%s);" % self.result()) code.putln("__Pyx_INCREF((PyObject*)%s);" % self.result())
elif self.memslice_slice:
self.put_memoryviewslice_slice_code(code)
elif self.is_temp: elif self.is_temp:
if self.type.is_pyobject: if self.type.is_pyobject:
if self.index.type.is_int: if self.index.type.is_int:
...@@ -2677,7 +2765,7 @@ class IndexNode(ExprNode): ...@@ -2677,7 +2765,7 @@ class IndexNode(ExprNode):
self.extra_index_params(), self.extra_index_params(),
code.error_goto(self.pos))) 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) assert isinstance(self.index, EllipsisNode)
import MemoryView import MemoryView
util_code = MemoryView.CopyContentsFuncUtilCode(rhs.type, self.type) util_code = MemoryView.CopyContentsFuncUtilCode(rhs.type, self.type)
...@@ -2687,7 +2775,7 @@ class IndexNode(ExprNode): ...@@ -2687,7 +2775,7 @@ class IndexNode(ExprNode):
def generate_buffer_setitem_code(self, rhs, code, op=""): def generate_buffer_setitem_code(self, rhs, code, op=""):
# Used from generate_assignment_code and InPlaceAssignmentNode # 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) self.put_nonecheck(code)
buffer_entry, ptrexpr = self.buffer_lookup_code(code) buffer_entry, ptrexpr = self.buffer_lookup_code(code)
...@@ -2712,10 +2800,13 @@ class IndexNode(ExprNode): ...@@ -2712,10 +2800,13 @@ class IndexNode(ExprNode):
def generate_assignment_code(self, rhs, code): def generate_assignment_code(self, rhs, code):
self.generate_subexpr_evaluation_code(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) 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: elif self.is_memoryviewslice_access:
self.generate_memoryviewslice_setitem_code(rhs, code) self.generate_memoryviewslice_copy_code(rhs, code)
elif self.type.is_pyobject: elif self.type.is_pyobject:
self.generate_setitem_code(rhs.py_result(), code) self.generate_setitem_code(rhs.py_result(), code)
else: else:
...@@ -2750,6 +2841,23 @@ class IndexNode(ExprNode): ...@@ -2750,6 +2841,23 @@ class IndexNode(ExprNode):
self.generate_subexpr_disposal_code(code) self.generate_subexpr_disposal_code(code)
self.free_subexpr_temps(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): def buffer_lookup_code(self, code):
# Assign indices to temps # Assign indices to temps
index_temps = [code.funcstate.allocate_temp(i.type, manage_ref=False) index_temps = [code.funcstate.allocate_temp(i.type, manage_ref=False)
...@@ -2761,21 +2869,13 @@ class IndexNode(ExprNode): ...@@ -2761,21 +2869,13 @@ class IndexNode(ExprNode):
# Generate buffer access code using these temps # Generate buffer access code using these temps
import Buffer, MemoryView import Buffer, MemoryView
if self.base.is_name: buffer_entry = self.buffer_entry()
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: if buffer_entry.type.is_buffer:
buffer_entry = Buffer.BufferEntry(entry)
negative_indices = entry.type.negative_indices negative_indices = entry.type.negative_indices
else: else:
buffer_entry = MemoryView.MemoryViewSliceBufferEntry(entry)
negative_indices = Buffer.buffer_defaults['negative_indices'] negative_indices = Buffer.buffer_defaults['negative_indices']
return buffer_entry, Buffer.put_buffer_lookup_code( return buffer_entry, Buffer.put_buffer_lookup_code(
entry=buffer_entry, entry=buffer_entry,
index_signeds=[i.type.signed for i in self.indices], index_signeds=[i.type.signed for i in self.indices],
...@@ -2784,6 +2884,14 @@ class IndexNode(ExprNode): ...@@ -2784,6 +2884,14 @@ class IndexNode(ExprNode):
pos=self.pos, code=code, pos=self.pos, code=code,
negative_indices=negative_indices) 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): def put_nonecheck(self, code):
code.globalstate.use_utility_code(raise_noneindex_error_utility_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)) code.putln("if (%s) {" % code.unlikely("%s == Py_None") % self.base.result_as(PyrexTypes.py_object_type))
...@@ -2791,6 +2899,7 @@ class IndexNode(ExprNode): ...@@ -2791,6 +2899,7 @@ class IndexNode(ExprNode):
code.putln(code.error_goto(self.pos)) code.putln(code.error_goto(self.pos))
code.putln("}") code.putln("}")
class SliceIndexNode(ExprNode): class SliceIndexNode(ExprNode):
# 2-element slice indexing # 2-element slice indexing
# #
...@@ -3928,6 +4037,9 @@ class AttributeNode(ExprNode): ...@@ -3928,6 +4037,9 @@ class AttributeNode(ExprNode):
if obj_type.has_attributes: if obj_type.has_attributes:
entry = None entry = None
if obj_type.attributes_known(): 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) entry = obj_type.scope.lookup_here(self.attribute)
if entry and entry.is_member: if entry and entry.is_member:
entry = None entry = None
...@@ -7384,8 +7496,8 @@ class CmpNode(object): ...@@ -7384,8 +7496,8 @@ class CmpNode(object):
contains_utility_code = UtilityCode( contains_utility_code = UtilityCode(
proto=""" proto="""
static CYTHON_INLINE int __Pyx_NegateNonNeg(int b) { static CYTHON_INLINE int __Pyx_NegateNonNeg(int b) {
return unlikely(b < 0) ? b : !b; return unlikely(b < 0) ? b : !b;
} }
static CYTHON_INLINE PyObject* __Pyx_PyBoolOrNull_FromLong(long b) { static CYTHON_INLINE PyObject* __Pyx_PyBoolOrNull_FromLong(long b) {
return unlikely(b < 0) ? NULL : __Pyx_PyBool_FromLong(b); return unlikely(b < 0) ? NULL : __Pyx_PyBool_FromLong(b);
......
...@@ -104,11 +104,11 @@ def put_assign_to_memviewslice(lhs_cname, rhs_cname, memviewslicetype, code, ...@@ -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.memview = %s.memview;" % (lhs_cname, rhs_cname))
code.putln("%s.data = %s.data;" % (lhs_cname, rhs_cname)) code.putln("%s.data = %s.data;" % (lhs_cname, rhs_cname))
ndim = len(memviewslicetype.axes) for i in range(memviewslicetype.ndim):
for i in range(ndim): tup = (lhs_cname, i, rhs_cname, i)
code.putln("%s.shape[%d] = %s.shape[%d];" % (lhs_cname, i, rhs_cname, i)) code.putln("%s.shape[%d] = %s.shape[%d];" % tup)
code.putln("%s.strides[%d] = %s.strides[%d];" % (lhs_cname, i, rhs_cname, i)) code.putln("%s.strides[%d] = %s.strides[%d];" % tup)
code.putln("%s.suboffsets[%d] = %s.suboffsets[%d];" % (lhs_cname, i, rhs_cname, i)) code.putln("%s.suboffsets[%d] = %s.suboffsets[%d];" % tup)
def get_buf_flags(specs): def get_buf_flags(specs):
is_c_contig, is_f_contig = is_cf_contig(specs) is_c_contig, is_f_contig = is_cf_contig(specs)
...@@ -182,14 +182,18 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry): ...@@ -182,14 +182,18 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
return self._for_all_ndim("%s.shape[%d]") return self._for_all_ndim("%s.shape[%d]")
def generate_buffer_lookup_code(self, code, index_cnames): 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 bufp = self.buf_ptr
type_decl = self.type.dtype.declaration_code("") 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) shape = "%s.shape[%d]" % (self.cname, dim)
stride = "%s.strides[%d]" % (self.cname, dim) stride = "%s.strides[%d]" % (self.cname, dim)
suboffset = "%s.suboffsets[%d]" % (self.cname, dim) suboffset = "%s.suboffsets[%d]" % (self.cname, dim)
index = index_cnames[dim]
flag = get_memoryview_flag(access, packing) flag = get_memoryview_flag(access, packing)
...@@ -219,7 +223,214 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry): ...@@ -219,7 +223,214 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
bufp = '( /* dim=%d */ %s )' % (dim, bufp) 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): def get_memoryview_flag(access, packing):
if access == 'full' and packing in ('strided', 'follow'): if access == 'full' and packing in ('strided', 'follow'):
...@@ -250,7 +461,7 @@ def get_copy_contents_name(from_mvs, to_mvs): ...@@ -250,7 +461,7 @@ def get_copy_contents_name(from_mvs, to_mvs):
class IsContigFuncUtilCode(object): class IsContigFuncUtilCode(object):
requires = None requires = None
def __init__(self, c_or_f): def __init__(self, c_or_f):
...@@ -337,7 +548,7 @@ static int %(copy_to_name)s(const __Pyx_memviewslice from_mvs, __Pyx_memviewslic ...@@ -337,7 +548,7 @@ static int %(copy_to_name)s(const __Pyx_memviewslice from_mvs, __Pyx_memviewslic
''' '''
class CopyContentsFuncUtilCode(object): class CopyContentsFuncUtilCode(object):
requires = None requires = None
def __init__(self, from_memview, to_memview): def __init__(self, from_memview, to_memview):
...@@ -587,7 +798,7 @@ def get_axes_specs(env, axes): ...@@ -587,7 +798,7 @@ def get_axes_specs(env, axes):
if not isinstance(axis.stop, NoneNode): if not isinstance(axis.stop, NoneNode):
raise CompileError(axis.stop.pos, STOP_ERR) 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. # a single axis spec, or an IntBinopNode.
if isinstance(axis.step, NoneNode): if isinstance(axis.step, NoneNode):
if is_c_contig or is_f_contig: if is_c_contig or is_f_contig:
...@@ -598,7 +809,7 @@ def get_axes_specs(env, axes): ...@@ -598,7 +809,7 @@ def get_axes_specs(env, axes):
elif isinstance(axis.step, IntNode): elif isinstance(axis.step, IntNode):
if idx not in (0, len(axes)-1): if idx not in (0, len(axes)-1):
raise CompileError(axis.step.pos, ONE_ERR) 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. # all others are cf_packing.
axes_specs.append((cf_access, 'contig')) axes_specs.append((cf_access, 'contig'))
...@@ -636,7 +847,7 @@ def is_cf_contig(specs): ...@@ -636,7 +847,7 @@ def is_cf_contig(specs):
# c_contiguous: 'follow', 'follow', ..., 'follow', 'contig' # c_contiguous: 'follow', 'follow', ..., 'follow', 'contig'
is_c_contig = True is_c_contig = True
elif (len(specs) > 1 and elif (len(specs) > 1 and
specs[0] == ('direct','contig') and specs[0] == ('direct','contig') and
all([axis == ('direct','follow') for axis in specs[1:]])): all([axis == ('direct','follow') for axis in specs[1:]])):
# f_contiguous: 'contig', 'follow', 'follow', ..., 'follow' # f_contiguous: 'contig', 'follow', 'follow', ..., 'follow'
...@@ -677,7 +888,7 @@ def validate_axes_specs(positions, specs): ...@@ -677,7 +888,7 @@ def validate_axes_specs(positions, specs):
access_specs = ('direct', 'ptr', 'full') access_specs = ('direct', 'ptr', 'full')
is_c_contig, is_f_contig = is_cf_contig(specs) is_c_contig, is_f_contig = is_cf_contig(specs)
has_contig = has_follow = has_strided = has_generic_contig = False has_contig = has_follow = has_strided = has_generic_contig = False
for pos, (access, packing) in zip(positions, specs): for pos, (access, packing) in zip(positions, specs):
......
...@@ -192,7 +192,6 @@ def create_pipeline(context, mode, exclude_classes=()): ...@@ -192,7 +192,6 @@ def create_pipeline(context, mode, exclude_classes=()):
FinalOptimizePhase(context), FinalOptimizePhase(context),
GilCheck(), GilCheck(),
UseUtilityCodeDefinitions(context), UseUtilityCodeDefinitions(context),
# PrintTree(),
] ]
filtered_stages = [] filtered_stages = []
for s in stages: for s in stages:
...@@ -293,7 +292,9 @@ def insert_into_pipeline(pipeline, transform, before=None, after=None): ...@@ -293,7 +292,9 @@ def insert_into_pipeline(pipeline, transform, before=None, after=None):
# Running a pipeline # Running a pipeline
# #
def run_pipeline(pipeline, source): def run_pipeline(pipeline, source, printtree=True):
from Cython.Compiler.Visitor import PrintTree
error = None error = None
data = source data = source
try: try:
...@@ -303,6 +304,8 @@ def run_pipeline(pipeline, source): ...@@ -303,6 +304,8 @@ def run_pipeline(pipeline, source):
if DebugFlags.debug_verbose_pipeline: if DebugFlags.debug_verbose_pipeline:
t = time() t = time()
print "Entering pipeline phase %r" % phase print "Entering pipeline phase %r" % phase
if not printtree and isinstance(phase, PrintTree):
continue
data = phase(data) data = phase(data)
if DebugFlags.debug_verbose_pipeline: if DebugFlags.debug_verbose_pipeline:
print " %.3f seconds" % (time() - t) print " %.3f seconds" % (time() - t)
......
...@@ -187,7 +187,7 @@ class PyrexType(BaseType): ...@@ -187,7 +187,7 @@ class PyrexType(BaseType):
def needs_nonecheck(self): def needs_nonecheck(self):
return 0 return 0
def public_decl(base_code, dll_linkage): def public_decl(base_code, dll_linkage):
if dll_linkage: if dll_linkage:
return "%s(%s)" % (dll_linkage, base_code) return "%s(%s)" % (dll_linkage, base_code)
...@@ -414,8 +414,7 @@ class MemoryViewSliceType(PyrexType): ...@@ -414,8 +414,7 @@ class MemoryViewSliceType(PyrexType):
def attributes_known(self): def attributes_known(self):
if self.scope is None: if self.scope is None:
import Symtab
import Symtab, MemoryView, Options
self.scope = scope = Symtab.CClassScope( self.scope = scope = Symtab.CClassScope(
'mvs_class_'+self.specialization_suffix(), 'mvs_class_'+self.specialization_suffix(),
...@@ -424,8 +423,17 @@ class MemoryViewSliceType(PyrexType): ...@@ -424,8 +423,17 @@ class MemoryViewSliceType(PyrexType):
scope.parent_type = self 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', scope.declare_var('shape',
c_array_type(c_py_ssize_t_type, c_array_type(c_py_ssize_t_type,
Options.buffer_max_dims), Options.buffer_max_dims),
...@@ -433,6 +441,7 @@ class MemoryViewSliceType(PyrexType): ...@@ -433,6 +441,7 @@ class MemoryViewSliceType(PyrexType):
cname='shape', cname='shape',
is_cdef=1) is_cdef=1)
elif attribute == 'strides':
scope.declare_var('strides', scope.declare_var('strides',
c_array_type(c_py_ssize_t_type, c_array_type(c_py_ssize_t_type,
Options.buffer_max_dims), Options.buffer_max_dims),
...@@ -440,6 +449,7 @@ class MemoryViewSliceType(PyrexType): ...@@ -440,6 +449,7 @@ class MemoryViewSliceType(PyrexType):
cname='strides', cname='strides',
is_cdef=1) is_cdef=1)
elif attribute == 'suboffsets':
scope.declare_var('suboffsets', scope.declare_var('suboffsets',
c_array_type(c_py_ssize_t_type, c_array_type(c_py_ssize_t_type,
Options.buffer_max_dims), Options.buffer_max_dims),
...@@ -447,6 +457,7 @@ class MemoryViewSliceType(PyrexType): ...@@ -447,6 +457,7 @@ class MemoryViewSliceType(PyrexType):
cname='suboffsets', cname='suboffsets',
is_cdef=1) is_cdef=1)
elif attribute in ("copy", "copy_fortran"):
ndim = len(self.axes) ndim = len(self.axes)
to_axes_c = [('direct', 'contig')] to_axes_c = [('direct', 'contig')]
......
...@@ -118,7 +118,7 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -118,7 +118,7 @@ class CythonUtilityCode(Code.UtilityCodeBase):
pipeline = Pipeline.insert_into_pipeline(pipeline, scope_transform, pipeline = Pipeline.insert_into_pipeline(pipeline, scope_transform,
before=transform) before=transform)
(err, tree) = Pipeline.run_pipeline(pipeline, tree) (err, tree) = Pipeline.run_pipeline(pipeline, tree, printtree=False)
assert not err, err assert not err, err
return tree return tree
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
typedef struct { typedef struct {
struct {{memview_struct_name}} *memview; struct {{memview_struct_name}} *memview;
/* For convenience and faster access */
char *data; char *data;
Py_ssize_t shape[{{max_dims}}]; Py_ssize_t shape[{{max_dims}}];
Py_ssize_t strides[{{max_dims}}]; Py_ssize_t strides[{{max_dims}}];
...@@ -30,9 +29,6 @@ static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *); ...@@ -30,9 +29,6 @@ static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *);
#define __Pyx_IS_C_CONTIG 1 #define __Pyx_IS_C_CONTIG 1
#define __Pyx_IS_F_CONTIG 2 #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, static int __Pyx_ValidateAndInit_memviewslice(struct __pyx_memoryview_obj *memview,
int *axes_specs, int c_or_f_flag, int ndim, __Pyx_TypeInfo *dtype, int *axes_specs, int c_or_f_flag, int ndim, __Pyx_TypeInfo *dtype,
__Pyx_BufFmt_StackElem stack[], __Pyx_memviewslice *memviewslice); __Pyx_BufFmt_StackElem stack[], __Pyx_memviewslice *memviewslice);
...@@ -170,7 +166,7 @@ static int __Pyx_ValidateAndInit_memviewslice( ...@@ -170,7 +166,7 @@ static int __Pyx_ValidateAndInit_memviewslice(
} }
if (spec & __Pyx_MEMVIEW_PTR) { 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, PyErr_Format(PyExc_ValueError,
"Buffer is not indirectly accessisble in dimension %d.", i); "Buffer is not indirectly accessisble in dimension %d.", i);
goto fail; goto fail;
...@@ -287,7 +283,6 @@ static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice, ...@@ -287,7 +283,6 @@ static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice,
struct {{memview_struct_name}} *memview = memslice->memview; struct {{memview_struct_name}} *memview = memslice->memview;
if (!memview) if (!memview)
return; /* allow uninitialized memoryview assignment */ return; /* allow uninitialized memoryview assignment */
/* __pyx_fatalerror("memoryslice is not initialized (line %d)", lineno); */
if (memview->acquisition_count <= 0) if (memview->acquisition_count <= 0)
__pyx_fatalerror("Acquisition count is %d (line %d)", __pyx_fatalerror("Acquisition count is %d (line %d)",
...@@ -324,6 +319,7 @@ static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice, ...@@ -324,6 +319,7 @@ static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice,
last_time = (memview->acquisition_count-- == 1); last_time = (memview->acquisition_count-- == 1);
PyThread_release_lock(memview->lock); PyThread_release_lock(memview->lock);
memslice->data = NULL;
if (last_time) { if (last_time) {
if (have_gil) { if (have_gil) {
Py_CLEAR(memslice->memview); Py_CLEAR(memslice->memview);
...@@ -332,6 +328,7 @@ static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice, ...@@ -332,6 +328,7 @@ static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice,
Py_CLEAR(memslice->memview); Py_CLEAR(memslice->memview);
PyGILState_Release(_gilstate); PyGILState_Release(_gilstate);
} }
} else {
memslice->memview = NULL; memslice->memview = NULL;
} }
} }
...@@ -466,4 +463,4 @@ int {{set_function}}(const char *itemp, PyObject *obj) { ...@@ -466,4 +463,4 @@ int {{set_function}}(const char *itemp, PyObject *obj) {
Py_DECREF(*(PyObject **) itemp); Py_DECREF(*(PyObject **) itemp);
*(PyObject **) itemp = obj; *(PyObject **) itemp = obj;
return 1; return 1;
} }
\ No newline at end of file
...@@ -5,7 +5,8 @@ from __future__ import unicode_literals ...@@ -5,7 +5,8 @@ from __future__ import unicode_literals
from cython cimport array from cython cimport array
cimport cython as cy cimport cython as cy
from libc.stdlib cimport malloc, free include "cythonarrayutil.pxi"
def contiguity(): def contiguity():
''' '''
...@@ -67,26 +68,6 @@ def dont_allocate_buffer(): ...@@ -67,26 +68,6 @@ def dont_allocate_buffer():
result.callback_free_data = callback result.callback_free_data = callback
result = None 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(): def test_cython_array_getbuffer():
""" """
>>> 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): ...@@ -23,6 +23,7 @@ def testcase(func):
include "mockbuffers.pxi" include "mockbuffers.pxi"
include "cythonarrayutil.pxi"
# #
# Buffer acquire and release tests # Buffer acquire and release tests
...@@ -1083,12 +1084,14 @@ def buffer_nogil(): ...@@ -1083,12 +1084,14 @@ def buffer_nogil():
# #
### Test cdef functions ### 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 'cdef called'
print buf1[6], buf2[1, 0] print buf1[6], buf2[1, 0]
buf2[1, 0] = "eggs" buf2[1, 0] = "eggs"
@testcase
def test_cdef_function(o1, o2=None): def test_cdef_function(o1, o2=None):
""" """
>>> A = IntMockBuffer("A", range(10)) >>> A = IntMockBuffer("A", range(10))
...@@ -1101,19 +1104,22 @@ def test_cdef_function(o1, o2=None): ...@@ -1101,19 +1104,22 @@ def test_cdef_function(o1, o2=None):
cdef called cdef called
6 eggs 6 eggs
released A 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) >>> test_cdef_function(A, B)
acquired A acquired A
acquired B
cdef called cdef called
6 eggs 6 eggs
released A released A
released B
acquired A acquired A
acquired B
cdef called cdef called
6 eggs 6 eggs
released A released A
acquired A
acquired B
cdef called
6 1
released A
released B released B
""" """
cdef_function(o1) cdef_function(o1)
...@@ -1122,15 +1128,15 @@ def test_cdef_function(o1, o2=None): ...@@ -1122,15 +1128,15 @@ def test_cdef_function(o1, o2=None):
if o2: if o2:
cdef_function(o1, o2) cdef_function(o1, o2)
cdef int[:] global_A = IntMockBuffer("A", range(10)) cdef int[:] global_A = IntMockBuffer("Global_A", range(10))
cdef object[::view.indirect, :] global_B = ObjectMockBuffer( cdef object[::view.indirect, :] global_B = ObjectMockBuffer(None, objs)
None, [["spam"],["ham"],["eggs"]])
cdef cdef_function2(int[:] buf1, object[::view.indirect, :] buf2 = global_B): cdef cdef_function2(int[:] buf1, object[::view.indirect, :] buf2 = global_B):
print 'cdef2 called' print 'cdef2 called'
print buf1[6], buf2[1, 0] print buf1[6], buf2[1, 0]
buf2[1, 0] = "eggs" buf2[1, 0] = "eggs"
@testcase
def test_cdef_function2(): def test_cdef_function2():
""" """
>>> test_cdef_function2() >>> test_cdef_function2()
...@@ -1151,3 +1157,76 @@ def test_cdef_function2(): ...@@ -1151,3 +1157,76 @@ def test_cdef_function2():
print global_B[1, 0] print global_B[1, 0]
cdef_function2(global_A, global_B) 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