diff options
author | Kurt Smith <kwsmith1@wisc.edu> | 2009-08-10 22:39:13 -0500 |
---|---|---|
committer | Kurt Smith <kwsmith1@wisc.edu> | 2009-08-10 22:39:13 -0500 |
commit | 59cc6a61fbb6034bc853150a5d4e486fb3c7bea1 (patch) | |
tree | b8d953aac7b943e7cfc0fe4023c42fb004c2f941 | |
parent | 095a925f40109de4d22a3a869a4184b1ec4f8c3a (diff) | |
download | cython-59cc6a61fbb6034bc853150a5d4e486fb3c7bea1.tar.gz |
memviewslice copy implementation.
-rw-r--r-- | Cython/Compiler/CythonScope.py | 15 | ||||
-rw-r--r-- | Cython/Compiler/MemoryView.py | 179 | ||||
-rw-r--r-- | Cython/Compiler/PyrexTypes.py | 107 |
3 files changed, 262 insertions, 39 deletions
diff --git a/Cython/Compiler/CythonScope.py b/Cython/Compiler/CythonScope.py index 41f7bd1b5..931843f96 100644 --- a/Cython/Compiler/CythonScope.py +++ b/Cython/Compiler/CythonScope.py @@ -177,13 +177,16 @@ cdef extern from *: cdef class memoryview(object): + cdef object obj cdef Py_buffer view cdef int gotbuf_flag 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): + self.obj = None __Pyx_ReleaseBuffer(&self.view) cdef memoryview memoryview_cwrapper(object o, int flags): @@ -215,7 +218,7 @@ cdef class array: Py_ssize_t itemsize 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.itemsize = itemsize @@ -245,7 +248,7 @@ cdef class array: idx += 1 assert idx == self.ndim - if mode == "fortran": + if mode == "f": idx = 0; stride = 1 for dim in shape: self.strides[idx] = stride*itemsize @@ -264,7 +267,7 @@ cdef class array: assert idx == -1 self.len = stride * itemsize 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 @@ -277,7 +280,7 @@ cdef class array: cdef int bufmode = -1 if self.mode == "c": bufmode = PyBUF_C_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS - elif self.mode == "fortran": + elif self.mode == "f": bufmode = PyBUF_F_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS if not (flags & bufmode): raise ValueError("Can only create a buffer that is contiguous in memory.") @@ -310,7 +313,7 @@ cdef class array: self.format = NULL 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) ''', prefix=cyarray_prefix) diff --git a/Cython/Compiler/MemoryView.py b/Cython/Compiler/MemoryView.py index f04dbb573..4f960dab4 100644 --- a/Cython/Compiler/MemoryView.py +++ b/Cython/Compiler/MemoryView.py @@ -69,6 +69,14 @@ def put_init_entry(mv_cname, code): code.putln("%s.data = 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): # import MemoryView assert rhs.type.is_memoryviewslice @@ -79,6 +87,7 @@ def gen_acquire_memoryviewslice(rhs, lhs_type, lhs_is_cglobal, lhs_result, lhs_p else: rhstmp = code.funcstate.allocate_temp(lhs_type, manage_ref=False) 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(): code.put_incref("%s.memview" % rhstmp, cython_memoryview_ptr_type) @@ -184,6 +193,157 @@ def src_conforms_to_dst(src, dst): 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): ''' @@ -288,16 +448,17 @@ def get_axes_specs(env, axes): def is_cf_contig(specs): 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 - all(axis[packing_idx] == 'follow' for axis in specs[:-1])): + elif (specs[-1] == ('direct','contig') and + all(axis == ('direct','follow') for axis in specs[:-1])): # c_contiguous: 'follow', 'follow', ..., 'follow', 'contig' is_c_contig = True elif (len(specs) > 1 and - specs[0][packing_idx] == 'contig' and - all(axis[packing_idx] == 'follow' for axis in specs[1:])): + specs[0] == ('direct','contig') and + all(axis == ('direct','follow') for axis in specs[1:])): # f_contiguous: 'contig', 'follow', 'follow', ..., 'follow' is_f_contig = True @@ -596,7 +757,13 @@ static int __Pyx_init_memviewslice( int i, retval=-1; 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; } diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index d97dd497d..40ce7e6c2 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -294,6 +294,7 @@ class MemoryViewSliceType(PyrexType): if self.scope is None: import Symtab, MemoryView + from MemoryView import axes_to_str self.scope = scope = Symtab.CClassScope( 'mvs_class_'+self.specialization_suffix(), @@ -302,41 +303,90 @@ class MemoryViewSliceType(PyrexType): scope.parent_type = self - # the C copy method - 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) + scope.declare_var('_data', c_char_ptr_type, None, cname='data', is_cdef=1) - # ensure the right util code is used - MemoryView.use_cython_array(self.env) - MemoryView.use_memview_util_code(self.env) + mangle_dtype = MemoryView.mangle_dtype_name(self.dtype) + ndim = len(self.axes) - # C copy method implementation. - ccopy_util_code = UtilityCode() - # ccopy_util_code.proto =#XXX + 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() - 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): - return "".join([access[0]+packing[0] for (access, packing) in self.axes]) + c_copy_util_code.proto += ('static int %s' + '(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): - 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): code.putln("%s.data = NULL;" % entry.cname) @@ -1702,6 +1752,9 @@ cython_memoryview_type = CStructOrUnionType("__pyx_obj_memoryview", "struct", cython_memoryview_ptr_type = CPtrType(cython_memoryview_type) +memoryviewslice_type = CStructOrUnionType("__Pyx_memviewslice", "struct", + None, 1, "__Pyx_memviewslice") + error_type = ErrorType() unspecified_type = UnspecifiedType() |