Skip to content
Snippets Groups Projects
Commit 4b9ac6b13862 authored by Kurt Smith's avatar Kurt Smith
Browse files

memviewslice copy implementation.

parent 05b2fab001fb
No related branches found
No related tags found
No related merge requests found
...@@ -177,7 +177,8 @@ ...@@ -177,7 +177,8 @@
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):
...@@ -180,7 +181,8 @@ ...@@ -180,7 +181,8 @@
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):
...@@ -185,5 +187,6 @@ ...@@ -185,5 +187,6 @@
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):
...@@ -215,7 +218,7 @@ ...@@ -215,7 +218,7 @@
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
...@@ -245,7 +248,7 @@ ...@@ -245,7 +248,7 @@
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*itemsize self.strides[idx] = stride*itemsize
...@@ -264,7 +267,7 @@ ...@@ -264,7 +267,7 @@
assert idx == -1 assert idx == -1
self.len = stride * itemsize self.len = stride * 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
...@@ -277,7 +280,7 @@ ...@@ -277,7 +280,7 @@
cdef int bufmode = -1 cdef int bufmode = -1
if self.mode == "c": if self.mode == "c":
bufmode = PyBUF_C_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS bufmode = PyBUF_C_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS
elif self.mode == "fortran": elif self.mode == "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.")
...@@ -310,7 +313,7 @@ ...@@ -310,7 +313,7 @@
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 @@ ...@@ -69,6 +69,14 @@
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 @@ ...@@ -79,6 +87,7 @@
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 @@ ...@@ -184,6 +193,157 @@
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,5 +448,6 @@ ...@@ -288,5 +448,6 @@
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
...@@ -292,7 +453,7 @@ ...@@ -292,7 +453,7 @@
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
...@@ -295,9 +456,9 @@ ...@@ -295,9 +456,9 @@
# 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 @@ ...@@ -596,7 +757,13 @@
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;
} }
......
...@@ -294,6 +294,7 @@ ...@@ -294,6 +294,7 @@
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(),
...@@ -302,10 +303,37 @@ ...@@ -302,10 +303,37 @@
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', mangle_dtype = MemoryView.mangle_dtype_name(self.dtype)
CFuncType(cython_memoryview_ptr_type, ndim = len(self.axes)
to_axes_c = [('direct', 'contig')]
to_axes_f = [('direct', 'contig')]
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()
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)]), [CFuncTypeArg("memviewslice", self, None)]),
pos = None, pos = None,
defining = 1, defining = 1,
...@@ -309,5 +337,22 @@ ...@@ -309,5 +337,22 @@
[CFuncTypeArg("memviewslice", self, None)]), [CFuncTypeArg("memviewslice", self, None)]),
pos = None, pos = None,
defining = 1, defining = 1,
cname = c_copy_name) 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)
...@@ -313,14 +358,27 @@ ...@@ -313,14 +358,27 @@
# the Fortran copy method c_copy_util_code.proto += ('static int %s'
f_copy_name = '__Pyx_CopyBuffer_F_'+self.specialization_suffix() '(const __Pyx_memviewslice *,'
scope.declare_cfunction('copy_fortran', ' __Pyx_memviewslice *); /* proto */\n' %
CFuncType(cython_memoryview_ptr_type, (copy_contents_name_c,))
[CFuncTypeArg("memviewslice", self, None)]),
pos = None, c_copy_util_code.impl += \
defining = 1, MemoryView.get_copy_contents_code(self, to_memview_c, copy_contents_name_c)
cname = f_copy_name)
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 # ensure the right util code is used
MemoryView.use_cython_array(self.env) MemoryView.use_cython_array(self.env)
MemoryView.use_memview_util_code(self.env) MemoryView.use_memview_util_code(self.env)
...@@ -322,13 +380,7 @@ ...@@ -322,13 +380,7 @@
# ensure the right util code is used # ensure the right util code is used
MemoryView.use_cython_array(self.env) MemoryView.use_cython_array(self.env)
MemoryView.use_memview_util_code(self.env) MemoryView.use_memview_util_code(self.env)
# C copy method implementation.
ccopy_util_code = UtilityCode()
# ccopy_util_code.proto =#XXX
return True return True
...@@ -333,6 +385,3 @@ ...@@ -333,6 +385,3 @@
return True return True
def axes_to_str(self):
return "".join([access[0]+packing[0] for (access, packing) in self.axes])
def specialization_suffix(self): def specialization_suffix(self):
...@@ -338,5 +387,6 @@ ...@@ -338,5 +387,6 @@
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)
...@@ -1702,6 +1752,9 @@ ...@@ -1702,6 +1752,9 @@
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()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment