Commit 308c3516 authored by Kurt Smith's avatar Kurt Smith Committed by Mark Florisson

memviewslice copy implementation.

parent daa49dd4
...@@ -260,13 +260,16 @@ cdef extern from *: ...@@ -260,13 +260,16 @@ cdef extern from *:
cdef class memoryview(object): cdef class memoryview(object):
cdef object obj
cdef Py_buffer view cdef Py_buffer view
cdef int gotbuf_flag cdef int gotbuf_flag
def __cinit__(memoryview self, object obj, int flags): def __cinit__(memoryview self, object obj, int flags):
__Pyx_GetBuffer(obj, &self.view, flags) self.obj = obj
__Pyx_GetBuffer(self.obj, &self.view, flags)
def __dealloc__(memoryview self): def __dealloc__(memoryview self):
self.obj = None
__Pyx_ReleaseBuffer(&self.view) __Pyx_ReleaseBuffer(&self.view)
cdef memoryview memoryview_cwrapper(object o, int flags): cdef memoryview memoryview_cwrapper(object o, int flags):
...@@ -298,7 +301,7 @@ cdef class array: ...@@ -298,7 +301,7 @@ cdef class array:
Py_ssize_t itemsize Py_ssize_t itemsize
str mode str mode
def __cinit__(array self, tuple shape, Py_ssize_t itemsize, char *format, mode="c"): def __cinit__(array self, tuple shape, Py_ssize_t itemsize, char *format, str mode="c"):
self.ndim = len(shape) self.ndim = len(shape)
self.itemsize = itemsize self.itemsize = itemsize
...@@ -328,7 +331,7 @@ cdef class array: ...@@ -328,7 +331,7 @@ cdef class array:
idx += 1 idx += 1
assert idx == self.ndim assert idx == self.ndim
if mode == "fortran": if mode == "f":
idx = 0; stride = 1 idx = 0; stride = 1
for dim in shape: for dim in shape:
self.strides[idx] = stride self.strides[idx] = stride
...@@ -347,7 +350,7 @@ cdef class array: ...@@ -347,7 +350,7 @@ cdef class array:
assert idx == -1 assert idx == -1
self.len = stride * self.itemsize self.len = stride * self.itemsize
else: else:
raise ValueError("Invalid mode, expected 'c' or 'fortran', got %s" % mode) raise ValueError("Invalid mode, expected 'c' or 'f', got %s" % mode)
self.mode = mode self.mode = mode
...@@ -360,7 +363,7 @@ cdef class array: ...@@ -360,7 +363,7 @@ cdef class array:
cdef int bufmode = -1 cdef int bufmode = -1
if self.mode == b"c": if self.mode == b"c":
bufmode = PyBUF_C_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS bufmode = PyBUF_C_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS
elif self.mode == b"fortran": elif self.mode == b"f":
bufmode = PyBUF_F_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS bufmode = PyBUF_F_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS
if not (flags & bufmode): if not (flags & bufmode):
raise ValueError("Can only create a buffer that is contiguous in memory.") raise ValueError("Can only create a buffer that is contiguous in memory.")
...@@ -393,7 +396,7 @@ cdef class array: ...@@ -393,7 +396,7 @@ cdef class array:
self.format = NULL self.format = NULL
self.itemsize = 0 self.itemsize = 0
cdef array array_cwrapper(tuple shape, Py_ssize_t itemsize, char *format, char mode): cdef array array_cwrapper(tuple shape, Py_ssize_t itemsize, char *format, char *mode):
return array(shape, itemsize, format, mode) return array(shape, itemsize, format, mode)
''', prefix=cyarray_prefix) ''', prefix=cyarray_prefix)
...@@ -69,6 +69,14 @@ def put_init_entry(mv_cname, code): ...@@ -69,6 +69,14 @@ def put_init_entry(mv_cname, code):
code.putln("%s.data = NULL;" % mv_cname) code.putln("%s.data = NULL;" % mv_cname)
code.putln("%s.memview = NULL;" % mv_cname) code.putln("%s.memview = NULL;" % mv_cname)
def mangle_dtype_name(dtype):
# a dumb wrapper for now; move Buffer.mangle_dtype_name in here later?
import Buffer
return Buffer.mangle_dtype_name(dtype)
def axes_to_str(axes):
return "".join([access[0]+packing[0] for (access, packing) in axes])
def gen_acquire_memoryviewslice(rhs, lhs_type, lhs_is_cglobal, lhs_result, lhs_pos, code): def gen_acquire_memoryviewslice(rhs, lhs_type, lhs_is_cglobal, lhs_result, lhs_pos, code):
# import MemoryView # import MemoryView
assert rhs.type.is_memoryviewslice assert rhs.type.is_memoryviewslice
...@@ -79,6 +87,7 @@ def gen_acquire_memoryviewslice(rhs, lhs_type, lhs_is_cglobal, lhs_result, lhs_p ...@@ -79,6 +87,7 @@ def gen_acquire_memoryviewslice(rhs, lhs_type, lhs_is_cglobal, lhs_result, lhs_p
else: else:
rhstmp = code.funcstate.allocate_temp(lhs_type, manage_ref=False) rhstmp = code.funcstate.allocate_temp(lhs_type, manage_ref=False)
code.putln("%s = %s;" % (rhstmp, rhs.result_as(lhs_type))) code.putln("%s = %s;" % (rhstmp, rhs.result_as(lhs_type)))
code.putln(code.error_goto_if_null("%s.memview" % rhstmp, lhs_pos))
if not rhs.result_in_temp(): if not rhs.result_in_temp():
code.put_incref("%s.memview" % rhstmp, cython_memoryview_ptr_type) code.put_incref("%s.memview" % rhstmp, cython_memoryview_ptr_type)
...@@ -184,6 +193,157 @@ def src_conforms_to_dst(src, dst): ...@@ -184,6 +193,157 @@ def src_conforms_to_dst(src, dst):
return True return True
def get_copy_contents_name(from_mvs, to_mvs):
dtype = from_mvs.dtype
assert dtype == to_mvs.dtype
return ('__Pyx_BufferCopyContents_%s_%s_%s' %
(axes_to_str(from_mvs.axes),
axes_to_str(to_mvs.axes),
mangle_dtype_name(dtype)))
copy_template = '''
static __Pyx_memviewslice %(copy_name)s(const __Pyx_memviewslice from_mvs) {
int i;
__Pyx_memviewslice new_mvs = {0, 0};
struct __pyx_obj_memoryview *from_memview = from_mvs.memview;
Py_buffer *buf = &from_memview->view;
PyObject *shape_tuple = 0;
PyObject *temp_int = 0;
struct __pyx_obj_array *array_obj = 0;
struct __pyx_obj_memoryview *memview_obj = 0;
char mode[] = "%(mode)s";
__Pyx_SetupRefcountContext("%(copy_name)s");
shape_tuple = PyTuple_New((Py_ssize_t)(buf->ndim));
if(unlikely(!shape_tuple)) {
goto fail;
}
__Pyx_GOTREF(shape_tuple);
for(i=0; i<buf->ndim; i++) {
temp_int = PyInt_FromLong(buf->shape[i]);
if(unlikely(!temp_int)) {
goto fail;
} else {
PyTuple_SET_ITEM(shape_tuple, i, temp_int);
}
}
array_obj = __pyx_cythonarray_array_cwrapper(shape_tuple, %(sizeof_dtype)s, buf->format, mode);
if (unlikely(!array_obj)) {
goto fail;
}
__Pyx_GOTREF(array_obj);
memview_obj = __pyx_viewaxis_memoryview_cwrapper((PyObject *)array_obj, %(contig_flag)s);
if (unlikely(!memview_obj)) {
goto fail;
}
/* initialize new_mvs */
if (unlikely(-1 == __Pyx_init_memviewslice(memview_obj, buf->ndim, &new_mvs))) {
PyErr_SetString(PyExc_RuntimeError,
"Could not initialize new memoryviewslice object.");
goto fail;
}
if (unlikely(-1 == %(copy_contents_name)s(&from_mvs, &new_mvs))) {
/* PyErr_SetString(PyExc_RuntimeError,
"Could not copy contents of memoryview slice."); */
goto fail;
}
goto no_fail;
fail:
__Pyx_XDECREF(new_mvs.memview); new_mvs.memview = 0;
new_mvs.data = 0;
no_fail:
__Pyx_XDECREF(shape_tuple); shape_tuple = 0;
__Pyx_GOTREF(temp_int);
__Pyx_XDECREF(temp_int); temp_int = 0;
__Pyx_XDECREF(array_obj); array_obj = 0;
__Pyx_FinishRefcountContext();
return new_mvs;
}
'''
def get_copy_contents_code(from_mvs, to_mvs, cfunc_name):
assert from_mvs.dtype == to_mvs.dtype
assert len(from_mvs.axes) == len(to_mvs.axes)
ndim = len(from_mvs.axes)
# XXX: we only support direct access for now.
for (access, packing) in from_mvs.axes:
if access != 'direct':
raise NotImplementedError("only direct access supported currently.")
code = '''
static int %(cfunc_name)s(const __Pyx_memviewslice *from_mvs, __Pyx_memviewslice *to_mvs) {
char *to_buf = (char *)to_mvs->data;
char *from_buf = (char *)from_mvs->data;
struct __pyx_obj_memoryview *temp_memview = 0;
char *temp_data = 0;
''' % {'cfunc_name' : cfunc_name}
if to_mvs.is_c_contig:
start, stop, step = 0, ndim, 1
elif to_mvs.is_f_contig:
start, stop, step = ndim-1, -1, -1
else:
assert False
INDENT = " "
for i, idx in enumerate(range(start, stop, step)):
# the crazy indexing is to account for the fortran indexing.
# 'i' always goes up from zero to ndim-1.
# 'idx' is the same as 'i' for c_contig, and goes from ndim-1 to 0 for f_contig.
# this makes the loop code below identical in both cases.
code += INDENT+"Py_ssize_t i%d = 0, idx%d = 0;\n" % (i,i)
code += INDENT+"Py_ssize_t stride%(i)d = from_mvs->diminfo[%(idx)d].strides;\n" % {'i':i, 'idx':idx}
code += INDENT+"Py_ssize_t shape%(i)d = from_mvs->diminfo[%(idx)d].shape;\n" % {'i':i, 'idx':idx}
code += "\n"
# put down the nested for-loop.
for k in range(ndim):
code += INDENT*(k+1) + "for(i%(k)d=0; i%(k)d<shape%(k)d; i%(k)d++) {\n" % {'k' : k}
code += INDENT*(k+2) + "idx%(k)d = i%(k)d * stride%(k)d;\n" % {'k' : k}
# the inner part of the loop.
dtype_decl = from_mvs.dtype.declaration_code("")
last_idx = ndim-1
code += INDENT*ndim+"memcpy(to_buf, from_buf+idx%(last_idx)d, sizeof(%(dtype_decl)s));\n" % locals()
code += INDENT*ndim+"to_buf += sizeof(%(dtype_decl)s);\n" % locals()
# for-loop closing braces
for k in range(ndim-1, -1, -1):
code += INDENT*(k+1)+"}\n"
# init to_mvs->data and to_mvs->diminfo.
code += INDENT+"temp_memview = to_mvs->memview;\n"
code += INDENT+"temp_data = to_mvs->data;\n"
code += INDENT+"to_mvs->memview = 0; to_mvs->data = 0;\n"
code += INDENT+"if(unlikely(-1 == __Pyx_init_memviewslice(temp_memview, %d, to_mvs))) {\n" % (ndim,)
code += INDENT*2+"return -1;\n"
code += INDENT+"}\n"
code += INDENT + "return 0;\n"
code += '}\n'
return code
def get_axes_specs(env, axes): def get_axes_specs(env, axes):
''' '''
...@@ -288,16 +448,17 @@ def get_axes_specs(env, axes): ...@@ -288,16 +448,17 @@ def get_axes_specs(env, axes):
def is_cf_contig(specs): def is_cf_contig(specs):
is_c_contig = is_f_contig = False is_c_contig = is_f_contig = False
packing_idx = 1 if (len(specs) == 1 and specs == [('direct', 'contig')]):
is_c_contig = True
if (specs[-1][packing_idx] == 'contig' and elif (specs[-1] == ('direct','contig') and
all(axis[packing_idx] == 'follow' for axis in specs[:-1])): all(axis == ('direct','follow') for axis in specs[:-1])):
# 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][packing_idx] == 'contig' and specs[0] == ('direct','contig') and
all(axis[packing_idx] == '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'
is_f_contig = True is_f_contig = True
...@@ -596,7 +757,13 @@ static int __Pyx_init_memviewslice( ...@@ -596,7 +757,13 @@ static int __Pyx_init_memviewslice(
int i, retval=-1; int i, retval=-1;
Py_buffer *buf = &memview->view; Py_buffer *buf = &memview->view;
if(!buf || memviewslice->memview || memviewslice->data) { if(!buf) {
PyErr_SetString(PyExc_ValueError,
"buf is NULL.");
goto fail;
} else if(memviewslice->memview || memviewslice->data) {
PyErr_SetString(PyExc_ValueError,
"memviewslice is already initialized!");
goto fail; goto fail;
} }
......
...@@ -383,6 +383,7 @@ class MemoryViewSliceType(PyrexType): ...@@ -383,6 +383,7 @@ class MemoryViewSliceType(PyrexType):
if self.scope is None: if self.scope is None:
import Symtab, MemoryView import Symtab, MemoryView
from MemoryView import axes_to_str
self.scope = scope = Symtab.CClassScope( self.scope = scope = Symtab.CClassScope(
'mvs_class_'+self.specialization_suffix(), 'mvs_class_'+self.specialization_suffix(),
...@@ -391,41 +392,90 @@ class MemoryViewSliceType(PyrexType): ...@@ -391,41 +392,90 @@ class MemoryViewSliceType(PyrexType):
scope.parent_type = self scope.parent_type = self
# the C copy method scope.declare_var('_data', c_char_ptr_type, None, cname='data', is_cdef=1)
c_copy_name = '__Pyx_CopyBuffer_C_'+self.specialization_suffix()
scope.declare_cfunction('copy',
CFuncType(cython_memoryview_ptr_type,
[CFuncTypeArg("memviewslice", self, None)]),
pos = None,
defining = 1,
cname = c_copy_name)
# the Fortran copy method
f_copy_name = '__Pyx_CopyBuffer_F_'+self.specialization_suffix()
scope.declare_cfunction('copy_fortran',
CFuncType(cython_memoryview_ptr_type,
[CFuncTypeArg("memviewslice", self, None)]),
pos = None,
defining = 1,
cname = f_copy_name)
# ensure the right util code is used mangle_dtype = MemoryView.mangle_dtype_name(self.dtype)
MemoryView.use_cython_array(self.env) ndim = len(self.axes)
MemoryView.use_memview_util_code(self.env)
# C copy method implementation. to_axes_c = [('direct', 'contig')]
ccopy_util_code = UtilityCode() to_axes_f = [('direct', 'contig')]
# ccopy_util_code.proto =#XXX if ndim-1:
to_axes_c = [('direct', 'follow')*(ndim-1)] + to_axes_c
to_axes_f = to_axes_f + [('direct', 'follow')*(ndim-1)]
to_memview_c = MemoryViewSliceType(self.dtype, to_axes_c, self.env)
to_memview_f = MemoryViewSliceType(self.dtype, to_axes_f, self.env)
copy_name_c = '__Pyx_BufferNew_C_From_'+self.specialization_suffix()
copy_name_f = '__Pyx_BufferNew_F_From_'+self.specialization_suffix()
return True cython_name_c = 'copy'
cython_name_f = 'copy_fortran'
c_copy_util_code = UtilityCode()
f_copy_util_code = UtilityCode()
for (to_memview, copy_name, cython_name, mode, contig_flag, util_code) in (
(to_memview_c, copy_name_c, cython_name_c, 'c', 'PyBUF_C_CONTIGUOUS', c_copy_util_code),
(to_memview_f, copy_name_f, cython_name_f, 'f', 'PyBUF_F_CONTIGUOUS', f_copy_util_code)):
copy_contents_name = MemoryView.get_copy_contents_name(self, to_memview)
scope.declare_cfunction(cython_name,
CFuncType(self,
[CFuncTypeArg("memviewslice", self, None)]),
pos = None,
defining = 1,
cname = copy_name)
copy_impl = MemoryView.copy_template %\
dict(copy_name=copy_name,
mode=mode,
sizeof_dtype="sizeof(%s)" % self.dtype.declaration_code(''),
contig_flag=contig_flag,
copy_contents_name=copy_contents_name)
copy_decl = '''\
static __Pyx_memviewslice %s(const __Pyx_memviewslice); /* proto */
''' % (copy_name,)
util_code.proto = copy_decl
util_code.impl = copy_impl
copy_contents_name_c = MemoryView.get_copy_contents_name(self, to_memview_c)
copy_contents_name_f = MemoryView.get_copy_contents_name(self, to_memview_f)
def axes_to_str(self): c_copy_util_code.proto += ('static int %s'
return "".join([access[0]+packing[0] for (access, packing) in self.axes]) '(const __Pyx_memviewslice *,'
' __Pyx_memviewslice *); /* proto */\n' %
(copy_contents_name_c,))
c_copy_util_code.impl += \
MemoryView.get_copy_contents_code(self, to_memview_c, copy_contents_name_c)
if (MemoryView.get_copy_contents_code(self, to_memview_c, copy_contents_name_c) !=
MemoryView.get_copy_contents_code(self, to_memview_f, copy_contents_name_f)):
f_copy_util_code.proto += ('static int %s'
'(const __Pyx_memviewslice *,'
' __Pyx_memviewslice *); /* proto */\n' %
(copy_contents_name_f,))
f_copy_util_code.impl += \
MemoryView.get_copy_contents_code(self, to_memview_f, copy_contents_name_f)
self.env.use_utility_code(c_copy_util_code)
self.env.use_utility_code(c_copy_util_code)
# ensure the right util code is used
MemoryView.use_cython_array(self.env)
MemoryView.use_memview_util_code(self.env)
return True
def specialization_suffix(self): def specialization_suffix(self):
return self.axes_to_str() + '_' + self.dtype.specalization_name() import MemoryView
return MemoryView.axes_to_str(self.axes) + '_' + MemoryView.mangle_dtype_name(self.dtype)
def global_init_code(self, entry, code): def global_init_code(self, entry, code):
code.putln("%s.data = NULL;" % entry.cname) code.putln("%s.data = NULL;" % entry.cname)
...@@ -2622,6 +2672,9 @@ cython_memoryview_type = CStructOrUnionType("__pyx_obj_memoryview", "struct", ...@@ -2622,6 +2672,9 @@ cython_memoryview_type = CStructOrUnionType("__pyx_obj_memoryview", "struct",
cython_memoryview_ptr_type = CPtrType(cython_memoryview_type) cython_memoryview_ptr_type = CPtrType(cython_memoryview_type)
memoryviewslice_type = CStructOrUnionType("__Pyx_memviewslice", "struct",
None, 1, "__Pyx_memviewslice")
error_type = ErrorType() error_type = ErrorType()
unspecified_type = UnspecifiedType() unspecified_type = UnspecifiedType()
......
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