Commit 917cce5e authored by Mark Florisson's avatar Mark Florisson

Support casting pointers to cython.array

parent d7322d2f
...@@ -5,7 +5,7 @@ from ExprNodes import * ...@@ -5,7 +5,7 @@ from ExprNodes import *
from StringEncoding import EncodedString from StringEncoding import EncodedString
from Errors import CompileError from Errors import CompileError
from UtilityCode import CythonUtilityCode from UtilityCode import CythonUtilityCode
from Code import UtilityCode from Code import UtilityCode, ContentHashingUtilityCode
import Cython.Compiler.Options import Cython.Compiler.Options
import Interpreter import Interpreter
import PyrexTypes import PyrexTypes
...@@ -34,10 +34,11 @@ class IntroduceBufferAuxiliaryVars(CythonTransform): ...@@ -34,10 +34,11 @@ class IntroduceBufferAuxiliaryVars(CythonTransform):
assert isinstance(node, ModuleNode) assert isinstance(node, ModuleNode)
self.max_ndim = 0 self.max_ndim = 0
result = super(IntroduceBufferAuxiliaryVars, self).__call__(node) result = super(IntroduceBufferAuxiliaryVars, self).__call__(node)
if self.buffers_exists or self.using_memoryview: if self.buffers_exists:
use_bufstruct_declare_code(node.scope) use_bufstruct_declare_code(node.scope)
use_py2_buffer_functions(node.scope) use_py2_buffer_functions(node.scope)
use_empty_bufstruct_code(node.scope, self.max_ndim) node.scope.use_utility_code(empty_bufstruct_utility)
return result return result
...@@ -476,13 +477,15 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives, ...@@ -476,13 +477,15 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives,
def use_bufstruct_declare_code(env): def use_bufstruct_declare_code(env):
env.use_utility_code(buffer_struct_declare_code) env.use_utility_code(buffer_struct_declare_code)
def use_empty_bufstruct_code(env, max_ndim):
def get_empty_bufstruct_code(max_ndim):
code = dedent(""" code = dedent("""
Py_ssize_t __Pyx_zeros[] = {%s}; Py_ssize_t __Pyx_zeros[] = {%s};
Py_ssize_t __Pyx_minusones[] = {%s}; Py_ssize_t __Pyx_minusones[] = {%s};
""") % (", ".join(["0"] * max_ndim), ", ".join(["-1"] * max_ndim)) """) % (", ".join(["0"] * max_ndim), ", ".join(["-1"] * max_ndim))
env.use_utility_code(UtilityCode(proto=code)) return UtilityCode(proto=code)
empty_bufstruct_utility = get_empty_bufstruct_code(Options.buffer_max_dims)
def buf_lookup_full_code(proto, defin, name, nd): def buf_lookup_full_code(proto, defin, name, nd):
""" """
...@@ -725,12 +728,18 @@ def get_type_information_cname(code, dtype, maxdepth=None): ...@@ -725,12 +728,18 @@ def get_type_information_cname(code, dtype, maxdepth=None):
print dtype print dtype
assert False assert False
typecode.putln(('static __Pyx_TypeInfo %s = { "%s", %s, sizeof(%s), \'%s\' };' if dtype.is_int:
is_unsigned = "IS_UNSIGNED(%s)" % declcode
else:
is_unsigned = "0"
typecode.putln(('static __Pyx_TypeInfo %s = { "%s", %s, sizeof(%s), \'%s\', %s };'
) % (name, ) % (name,
rep, rep,
structinfo_name, structinfo_name,
declcode, declcode,
typegroup, typegroup,
is_unsigned,
), safe=True) ), safe=True)
return name return name
...@@ -753,5 +762,11 @@ impl = """ ...@@ -753,5 +762,11 @@ impl = """
""") """)
raise_buffer_fallback_code = load_buffer_utility("BufferFallbackError") raise_buffer_fallback_code = load_buffer_utility("BufferFallbackError")
buffer_structs_code = load_buffer_utility("BufferFormatStructs")
acquire_utility_code = load_buffer_utility("BufferFormatCheck", acquire_utility_code = load_buffer_utility("BufferFormatCheck",
context=context) context=context,
\ No newline at end of file requires=[buffer_structs_code])
# See utility code BufferFormatFromTypeInfo
_typeinfo_to_format_code = load_buffer_utility(
"TypeInfoToFormat", context={}, requires=[buffer_structs_code])
\ No newline at end of file
...@@ -318,7 +318,12 @@ class ContentHashingUtilityCode(UtilityCode): ...@@ -318,7 +318,12 @@ class ContentHashingUtilityCode(UtilityCode):
return hash((self.proto, self.impl)) return hash((self.proto, self.impl))
def __eq__(self, other): def __eq__(self, other):
return (self.proto, self.impl) == (other.proto, other.impl) if not isinstance(other, type(self)):
return False
self_proto = getattr(self, 'proto', None)
other_proto = getattr(other, 'proto', None)
return (self_proto, self.impl) == (other_proto, other.impl)
class LazyUtilityCode(UtilityCodeBase): class LazyUtilityCode(UtilityCodeBase):
......
...@@ -71,12 +71,14 @@ class CythonScope(ModuleScope): ...@@ -71,12 +71,14 @@ class CythonScope(ModuleScope):
# self.test_cythonscope() # self.test_cythonscope()
def test_cythonscope(self): def test_cythonscope(self):
# A special function just to make it easy to test the scope and """
# utility code functionality in isolation. It is available to Creates some entries for testing purposes and entries for
# "end-users" but nobody will know it is there anyway... cython.array() and for cython.view.*.
"""
cython_testscope_utility_code.declare_in_scope(self) cython_testscope_utility_code.declare_in_scope(self)
cython_test_extclass_utility_code.declare_in_scope(self) cython_test_extclass_utility_code.declare_in_scope(self)
cython_array_utility_code.declare_in_scope(self)
MemoryView.cython_array_utility_code.declare_in_scope(self)
# #
# The view sub-scope # The view sub-scope
...@@ -92,7 +94,9 @@ class CythonScope(ModuleScope): ...@@ -92,7 +94,9 @@ class CythonScope(ModuleScope):
cythonview_testscope_utility_code.declare_in_scope(viewscope) cythonview_testscope_utility_code.declare_in_scope(viewscope)
view_utility_code.declare_in_scope(viewscope) view_utility_scope = MemoryView.view_utility_code.declare_in_scope(viewscope)
MemoryView.memview_fromslice_utility_code.from_scope = view_utility_scope
MemoryView.memview_fromslice_utility_code.declare_in_scope(viewscope)
def create_cython_scope(context, create_testscope): def create_cython_scope(context, create_testscope):
...@@ -133,14 +137,3 @@ cython_test_extclass_utility_code = \ ...@@ -133,14 +137,3 @@ cython_test_extclass_utility_code = \
test_cython_utility_dep]) test_cython_utility_dep])
cythonview_testscope_utility_code = load_testscope_utility("View.TestScope") cythonview_testscope_utility_code = load_testscope_utility("View.TestScope")
\ No newline at end of file
view_utility_code = MemoryView.load_memview_cy_utility(
"View.MemoryView", context=MemoryView.context,
requires=[Buffer.GetAndReleaseBufferUtilityCode(),
MemoryView.memviewslice_declare_code],
)
cython_array_utility_code = MemoryView.load_memview_cy_utility(
"CythonArray",
context=MemoryView.context,
requires=[view_utility_code])
...@@ -584,6 +584,11 @@ class ExprNode(Node): ...@@ -584,6 +584,11 @@ class ExprNode(Node):
if dst_type.is_reference: if dst_type.is_reference:
dst_type = dst_type.ref_base_type dst_type = dst_type.ref_base_type
if self.coercion_type is not None:
# This is purely for error checking purposes!
node = NameNode(self.pos, name='', type=self.coercion_type)
node.coerce_to(dst_type, env)
if dst_type.is_memoryviewslice: if dst_type.is_memoryviewslice:
import MemoryView import MemoryView
if not src.type.is_memoryviewslice: if not src.type.is_memoryviewslice:
...@@ -747,6 +752,7 @@ class PyConstNode(AtomicExprNode): ...@@ -747,6 +752,7 @@ class PyConstNode(AtomicExprNode):
class NoneNode(PyConstNode): class NoneNode(PyConstNode):
# The constant value None # The constant value None
is_none = 1
value = "Py_None" value = "Py_None"
constant_result = None constant_result = None
...@@ -6051,6 +6057,157 @@ class TypecastNode(ExprNode): ...@@ -6051,6 +6057,157 @@ class TypecastNode(ExprNode):
code.put_incref(self.result(), self.ctype()) code.put_incref(self.result(), self.ctype())
ERR_START = "Start may not be given"
ERR_NOT_STOP = "Stop must be provided to indicate shape"
ERR_STEPS = ("Strides may only be given to indicate contiguity. "
"Consider slicing it after conversion")
ERR_NOT_POINTER = "Can only create cython.array from pointer"
ERR_BASE_TYPE = "Pointer base type does not match cython.array base type"
class CythonArrayNode(ExprNode):
"""
Used when a pointer of base_type is cast to a memoryviewslice with that
base type. i.e.
<int[::1, :]> p
creates a fortran-contiguous cython.array.
We leave the type set to object so coercions to object are more efficient
and less work. Acquiring a memoryviewslice from this will be just as
efficient. ExprNode.coerce_to() will do the additional typecheck on
self.compile_time_type
"""
subexprs = ['operand', 'shapes']
shapes = None
is_temp = True
mode = "c"
shape_type = PyrexTypes.c_py_ssize_t_type
def analyse_types(self, env):
import MemoryView
self.type = error_type
self.env = env
self.shapes = []
for axis_no, axis in enumerate(self.base_type_node.axes):
if not axis.start.is_none:
return error(axis.start.pos, ERR_START)
if axis.stop.is_none:
return error(axis.pos, ERR_NOT_STOP)
axis.stop.analyse_types(env)
shape = axis.stop.coerce_to(self.shape_type, env)
if not shape.is_literal:
shape.coerce_to_temp(env)
self.shapes.append(shape)
if not axis.stop.type.is_int:
return error(axis.stop.pos, "Expected an integer type")
first_or_last = axis_no in (0, len(self.base_type_node.axes) - 1)
if not axis.step.is_none and first_or_last:
axis.step.analyse_types(env)
if (not axis.step.type.is_int and axis.step.is_literal and not
axis.step.type.is_error):
return error(axis.step.pos, "Expected an integer literal")
if axis.step.compile_time_value(env) != 1:
return error(axis.step.pos, ERR_STEPS)
if axis_no == 0:
self.mode = "fortran"
elif axis.step and not first_or_last:
return error(axis.step.pos, ERR_STEPS)
self.operand.analyse_types(env)
array_dtype = self.base_type_node.base_type_node.analyse(env)
if not self.operand.type.is_ptr:
return error(self.operand.pos, ERR_NOT_POINTER)
elif not self.operand.type.base_type.same_as(array_dtype):
return error(self.operand.pos, ERR_BASE_TYPE)
#self.operand = self.operand.coerce_to(PyrexTypes.c_char_ptr_type, env)
if not self.operand.is_name:
self.operand = self.operand.coerce_to_temp(env)
axes = [('direct', 'follow')] * len(self.base_type_node.axes)
if self.mode == "fortran":
axes[0] = ('direct', 'contig')
else:
axes[-1] = ('direct', 'contig')
self.coercion_type = PyrexTypes.MemoryViewSliceType(array_dtype, axes)
#self.type = py_object_type
self.type = env.global_scope().context.cython_scope.lookup("array").type
assert self.type
env.use_utility_code(MemoryView.cython_array_utility_code)
env.use_utility_code(MemoryView.typeinfo_to_format_code)
def allocate_temp_result(self, code):
if self.temp_code:
raise RuntimeError("temp allocated mulitple times")
self.temp_code = code.funcstate.allocate_temp(self.type, True)
def generate_result_code(self, code):
import Buffer
shapes = [self.shape_type.cast_code(shape.result())
for shape in self.shapes]
dtype = self.coercion_type.dtype
shapes_temp = code.funcstate.allocate_temp(py_object_type, True)
format_temp = code.funcstate.allocate_temp(py_object_type, True)
itemsize = "sizeof(%s)" % dtype.declaration_code("")
type_info = Buffer.get_type_information_cname(code, dtype)
code.putln("if (!%s) {" % self.operand.result())
code.putln( 'PyErr_SetString(PyExc_ValueError,'
'"Cannot create cython.array from NULL pointer");')
code.putln(code.error_goto(self.operand.pos))
code.putln("}")
code.putln("%s = __pyx_format_from_typeinfo(&%s);" %
(format_temp, type_info))
code.putln('%s = Py_BuildValue("(%s)", %s);' % (shapes_temp,
"n" * len(shapes),
", ".join(shapes)))
err = "!%s || !%s || !PyBytes_Check(%s)" % (format_temp, shapes_temp,
format_temp)
code.putln(code.error_goto_if(err, self.pos))
code.put_gotref(format_temp)
code.put_gotref(shapes_temp)
tup = (self.result(), shapes_temp, itemsize, format_temp,
self.mode, self.operand.result())
code.putln('%s = __pyx_array_new('
'%s, %s, PyBytes_AS_STRING(%s), '
'"%s", (char *) %s);' % tup)
code.putln(code.error_goto_if_null(self.result(), self.pos))
code.put_gotref(self.result())
def dispose(temp):
code.put_decref_clear(temp, py_object_type)
code.funcstate.release_temp(temp)
dispose(shapes_temp)
dispose(format_temp)
class SizeofNode(ExprNode): class SizeofNode(ExprNode):
# Abstract base class for sizeof(x) expression nodes. # Abstract base class for sizeof(x) expression nodes.
...@@ -7910,6 +8067,10 @@ class CoerceToPyTypeNode(CoercionNode): ...@@ -7910,6 +8067,10 @@ class CoerceToPyTypeNode(CoercionNode):
# FIXME: check that the target type and the resulting type are compatible # FIXME: check that the target type and the resulting type are compatible
pass pass
if arg.type.is_memoryviewslice:
# Register utility codes at this point
arg.type.get_to_py_function(env, arg)
self.env = env self.env = env
gil_message = "Converting to Python object" gil_message = "Converting to Python object"
......
...@@ -66,8 +66,6 @@ memview_typeptr_cname = '__pyx_memoryview_type' ...@@ -66,8 +66,6 @@ memview_typeptr_cname = '__pyx_memoryview_type'
memview_objstruct_cname = '__pyx_memoryview_obj' memview_objstruct_cname = '__pyx_memoryview_obj'
memviewslice_cname = u'__Pyx_memviewslice' memviewslice_cname = u'__Pyx_memviewslice'
def put_init_entry(mv_cname, code): 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)
...@@ -129,18 +127,9 @@ def get_buf_flags(specs): ...@@ -129,18 +127,9 @@ def get_buf_flags(specs):
else: else:
return memview_strided_access return memview_strided_access
def use_memview_util_code(env):
import CythonScope
env.use_utility_code(CythonScope.view_utility_code)
env.use_utility_code(memviewslice_declare_code)
def use_memview_cwrap(env):
import CythonScope
env.use_utility_code(CythonScope.view_utility_code)
def use_cython_array(env): def use_cython_array(env):
import CythonScope env.use_utility_code(cython_array_utility_code)
env.use_utility_code(CythonScope.cython_array_utility_code)
def src_conforms_to_dst(src, dst): def src_conforms_to_dst(src, dst):
''' '''
...@@ -778,7 +767,31 @@ memviewslice_declare_code = load_memview_c_utility( ...@@ -778,7 +767,31 @@ memviewslice_declare_code = load_memview_c_utility(
memviewslice_init_code = load_memview_c_utility( memviewslice_init_code = load_memview_c_utility(
"MemviewSliceInit", "MemviewSliceInit",
context=dict(context, BUF_MAX_NDIMS=Options.buffer_max_dims), context=dict(context, BUF_MAX_NDIMS=Options.buffer_max_dims),
requires=[memviewslice_declare_code, Buffer.acquire_utility_code], requires=[memviewslice_declare_code,
Buffer.acquire_utility_code],
) )
memviewslice_index_helpers = load_memview_c_utility("MemviewSliceIndex") memviewslice_index_helpers = load_memview_c_utility("MemviewSliceIndex")
typeinfo_to_format_code = load_memview_cy_utility(
"BufferFormatFromTypeInfo", requires=[Buffer._typeinfo_to_format_code])
view_utility_code = load_memview_cy_utility(
"View.MemoryView",
context=context,
requires=[Buffer.GetAndReleaseBufferUtilityCode(),
Buffer.buffer_struct_declare_code,
Buffer.empty_bufstruct_utility,
memviewslice_init_code],
)
cython_array_utility_code = load_memview_cy_utility(
"CythonArray",
context=context,
requires=[view_utility_code])
memview_fromslice_utility_code = load_memview_cy_utility(
"MemviewFromSlice",
context=context,
requires=[view_utility_code],
)
\ No newline at end of file
...@@ -2440,8 +2440,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -2440,8 +2440,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type.typeptr_cname, type.typeobj_cname)) type.typeptr_cname, type.typeobj_cname))
def generate_cfunction_declaration(entry, env, code, definition): def generate_cfunction_declaration(entry, env, code, definition):
from_cy_utility = entry.used and entry.utility_code_definition
if entry.inline_func_in_pxd or (not entry.in_cinclude and (definition if entry.inline_func_in_pxd or (not entry.in_cinclude and (definition
or entry.defined_in_pxd or entry.visibility == 'extern')): or entry.defined_in_pxd or entry.visibility == 'extern' or from_cy_utility)):
if entry.visibility == 'extern': if entry.visibility == 'extern':
storage_class = "%s " % Naming.extern_c_macro storage_class = "%s " % Naming.extern_c_macro
dll_linkage = "DL_IMPORT" dll_linkage = "DL_IMPORT"
......
...@@ -126,6 +126,7 @@ class Node(object): ...@@ -126,6 +126,7 @@ class Node(object):
__metaclass__ = VerboseCodeWriter __metaclass__ = VerboseCodeWriter
is_name = 0 is_name = 0
is_none = 0
is_literal = 0 is_literal = 0
is_terminator = 0 is_terminator = 0
temps = None temps = None
...@@ -137,6 +138,11 @@ class Node(object): ...@@ -137,6 +138,11 @@ class Node(object):
cf_state = None cf_state = None
# This may be an additional (or 'actual') type that will be checked when
# this node is coerced to another type. This could be useful to set when
# the actual type to which it can coerce is known, but you want to leave
# the type a py_object_type
coercion_type = None
def __init__(self, pos, **kw): def __init__(self, pos, **kw):
self.pos = pos self.pos = pos
...@@ -823,12 +829,18 @@ class MemoryViewSliceTypeNode(CBaseTypeNode): ...@@ -823,12 +829,18 @@ class MemoryViewSliceTypeNode(CBaseTypeNode):
return self.type return self.type
self.type = PyrexTypes.MemoryViewSliceType(base_type, axes_specs) self.type = PyrexTypes.MemoryViewSliceType(base_type, axes_specs)
MemoryView.use_memview_util_code(env) if self.type.dtype.is_memoryviewslice:
MemoryView.use_cython_array(env) error(self.pos, "Memoryview slices may not be used as the "
MemoryView.use_memview_util_code(env) "base type for memoryview slices")
env.use_utility_code(MemoryView.memviewslice_declare_code)
self.use_memview_utilities(env)
return self.type return self.type
def use_memview_utilities(self, env):
import MemoryView
env.use_utility_code(MemoryView.view_utility_code)
class CNestedBaseTypeNode(CBaseTypeNode): class CNestedBaseTypeNode(CBaseTypeNode):
# For C++ classes that live inside other C++ classes. # For C++ classes that live inside other C++ classes.
......
...@@ -297,7 +297,8 @@ def p_typecast(s): ...@@ -297,7 +297,8 @@ def p_typecast(s):
pos = s.position() pos = s.position()
s.next() s.next()
base_type = p_c_base_type(s) base_type = p_c_base_type(s)
if base_type.name is None: is_memslice = isinstance(base_type, Nodes.MemoryViewSliceTypeNode)
if not is_memslice and base_type.name is None:
s.error("Unknown type") s.error("Unknown type")
declarator = p_c_declarator(s, empty = 1) declarator = p_c_declarator(s, empty = 1)
if s.sy == '?': if s.sy == '?':
...@@ -307,6 +308,10 @@ def p_typecast(s): ...@@ -307,6 +308,10 @@ def p_typecast(s):
typecheck = 0 typecheck = 0
s.expect(">") s.expect(">")
operand = p_factor(s) operand = p_factor(s)
if is_memslice:
return ExprNodes.CythonArrayNode(pos, base_type_node=base_type,
operand=operand)
return ExprNodes.TypecastNode(pos, return ExprNodes.TypecastNode(pos,
base_type = base_type, base_type = base_type,
declarator = declarator, declarator = declarator,
......
...@@ -553,14 +553,15 @@ class MemoryViewSliceType(PyrexType): ...@@ -553,14 +553,15 @@ class MemoryViewSliceType(PyrexType):
return True return True
def get_to_py_function(self, env, obj): def get_to_py_function(self, env, obj):
import MemoryView
env.use_utility_code(MemoryView.memview_fromslice_utility_code)
to_py_func, from_py_func = self.dtype_object_conversion_funcs(env) to_py_func, from_py_func = self.dtype_object_conversion_funcs(env)
to_py_func = "(PyObject *(*)(char *)) " + to_py_func to_py_func = "(PyObject *(*)(char *)) " + to_py_func
from_py_func = "(int (*)(char *, PyObject *)) " + from_py_func from_py_func = "(int (*)(char *, PyObject *)) " + from_py_func
tup = (obj.result(), obj.result(), self.flags, self.ndim, tup = (obj.result(), self.ndim, to_py_func, from_py_func)
to_py_func, from_py_func) return "__pyx_memoryview_fromslice(&%s, %s, %s, %s);" % tup
return ("__pyx_memoryview_fromslice(&%s, %s.memview->obj, "
"%s, %s, %s, %s);" % tup)
def dtype_object_conversion_funcs(self, env): def dtype_object_conversion_funcs(self, env):
import MemoryView, Code import MemoryView, Code
......
...@@ -291,8 +291,6 @@ class Scope(object): ...@@ -291,8 +291,6 @@ class Scope(object):
entries = [(name, entry) entries = [(name, entry)
for name, entry in other.entries.iteritems() for name, entry in other.entries.iteritems()
if entry.used or merge_unused] if entry.used or merge_unused]
# !@#$ py23
entries = dict(entries)
self.entries.update(entries) self.entries.update(entries)
...@@ -305,8 +303,9 @@ class Scope(object): ...@@ -305,8 +303,9 @@ class Scope(object):
'cfunc_entries', 'cfunc_entries',
'c_class_entries'): 'c_class_entries'):
self_entries = getattr(self, attr) self_entries = getattr(self, attr)
names = set([e.name for e in self_entries])
for entry in getattr(other, attr): for entry in getattr(other, attr):
if entry.used or merge_unused: if (entry.used or merge_unused) and entry.name not in names:
self_entries.append(entry) self_entries.append(entry)
def __str__(self): def __str__(self):
......
...@@ -115,8 +115,8 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -115,8 +115,8 @@ class CythonUtilityCode(Code.UtilityCodeBase):
return module_node return module_node
transform = ParseTreeTransforms.AnalyseDeclarationsTransform transform = ParseTreeTransforms.AnalyseDeclarationsTransform
pipeline = Pipeline.insert_into_pipeline(pipeline, transform, pipeline = Pipeline.insert_into_pipeline(pipeline, scope_transform,
before=scope_transform) before=transform)
(err, tree) = Pipeline.run_pipeline(pipeline, tree) (err, tree) = Pipeline.run_pipeline(pipeline, tree)
assert not err, err assert not err, err
...@@ -125,7 +125,7 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -125,7 +125,7 @@ class CythonUtilityCode(Code.UtilityCodeBase):
def put_code(self, output): def put_code(self, output):
pass pass
def declare_in_scope(self, dest_scope, used=False, modname=None): def declare_in_scope(self, dest_scope, used=False):
""" """
Declare all entries from the utility code in dest_scope. Code will only Declare all entries from the utility code in dest_scope. Code will only
be included for used entries. If module_name is given, declare the be included for used entries. If module_name is given, declare the
...@@ -143,13 +143,12 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -143,13 +143,12 @@ class CythonUtilityCode(Code.UtilityCodeBase):
entry.utility_code_definition = self entry.utility_code_definition = self
entry.used = used entry.used = used
if modname and entry.type.is_extension_type: original_scope = tree.scope
entry.qualified_name = modname dest_scope.merge_in(original_scope, merge_unused=True)
entry.type.module_name = modname
dest_scope.merge_in(tree.scope, merge_unused=True)
tree.scope = dest_scope tree.scope = dest_scope
for dep in self.requires: for dep in self.requires:
if dep.is_cython_utility: if dep.is_cython_utility:
dep.declare_in_scope(dest_scope) dep.declare_in_scope(dest_scope)
return original_scope
...@@ -35,19 +35,9 @@ static void __Pyx_RaiseBufferFallbackError(void) { ...@@ -35,19 +35,9 @@ static void __Pyx_RaiseBufferFallbackError(void) {
"Buffer acquisition failed on assignment; and then reacquiring the old buffer failed too!"); "Buffer acquisition failed on assignment; and then reacquiring the old buffer failed too!");
} }
/////////////// BufferFormatCheck.proto /////////////// /////////////// BufferFormatStructs.proto ///////////////
{{#
Buffer format string checking
Buffer type checking. Utility code for checking that acquired
buffers match our assumptions. We only need to check ndim and
the format string; the access mode/flags is checked by the
exporter.
The alignment code is copied from _struct.c in Python.
}}
#define IS_UNSIGNED(type) (((type) -1) > 0)
/* Run-time type information about structs used with buffers */ /* Run-time type information about structs used with buffers */
struct __Pyx_StructField_; struct __Pyx_StructField_;
...@@ -57,6 +47,7 @@ typedef struct { ...@@ -57,6 +47,7 @@ typedef struct {
struct __Pyx_StructField_* fields; struct __Pyx_StructField_* fields;
size_t size; /* sizeof(type) */ size_t size; /* sizeof(type) */
char typegroup; /* _R_eal, _C_omplex, Signed _I_nt, _U_nsigned int, _S_truct, _P_ointer, _O_bject */ char typegroup; /* _R_eal, _C_omplex, Signed _I_nt, _U_nsigned int, _S_truct, _P_ointer, _O_bject */
char is_unsigned;
} __Pyx_TypeInfo; } __Pyx_TypeInfo;
typedef struct __Pyx_StructField_ { typedef struct __Pyx_StructField_ {
...@@ -82,6 +73,19 @@ typedef struct { ...@@ -82,6 +73,19 @@ typedef struct {
} __Pyx_BufFmt_Context; } __Pyx_BufFmt_Context;
/////////////// BufferFormatCheck.proto ///////////////
{{#
Buffer format string checking
Buffer type checking. Utility code for checking that acquired
buffers match our assumptions. We only need to check ndim and
the format string; the access mode/flags is checked by the
exporter.
The alignment code is copied from _struct.c in Python.
}}
static CYTHON_INLINE int __Pyx_GetBufferAndValidate(Py_buffer* buf, PyObject* obj, static CYTHON_INLINE int __Pyx_GetBufferAndValidate(Py_buffer* buf, PyObject* obj,
__Pyx_TypeInfo* dtype, int flags, int nd, int cast, __Pyx_BufFmt_StackElem* stack); __Pyx_TypeInfo* dtype, int flags, int nd, int cast, __Pyx_BufFmt_StackElem* stack);
static CYTHON_INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info); static CYTHON_INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info);
...@@ -246,6 +250,7 @@ static char __Pyx_BufFmt_TypeCharToGroup(char ch, int is_complex) { ...@@ -246,6 +250,7 @@ static char __Pyx_BufFmt_TypeCharToGroup(char ch, int is_complex) {
} }
} }
static void __Pyx_BufFmt_RaiseExpected(__Pyx_BufFmt_Context* ctx) { static void __Pyx_BufFmt_RaiseExpected(__Pyx_BufFmt_Context* ctx) {
if (ctx->head == NULL || ctx->head->field == &ctx->root) { if (ctx->head == NULL || ctx->head->field == &ctx->root) {
const char* expected; const char* expected;
...@@ -519,3 +524,60 @@ static CYTHON_INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info) { ...@@ -519,3 +524,60 @@ static CYTHON_INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info) {
if (info->suboffsets == __Pyx_minusones) info->suboffsets = NULL; if (info->suboffsets == __Pyx_minusones) info->suboffsets = NULL;
__Pyx_ReleaseBuffer(info); __Pyx_ReleaseBuffer(info);
} }
/////////////// TypeInfoToFormat.proto ///////////////
struct __pyx_typeinfo_string {
char string[3];
};
static struct __pyx_typeinfo_string __Pyx_TypeInfoToFormat(__Pyx_TypeInfo *type);
/////////////// TypeInfoToFormat ///////////////
{{# See also MemoryView.pyx:BufferFormatFromTypeInfo }}
static struct __pyx_typeinfo_string __Pyx_TypeInfoToFormat(__Pyx_TypeInfo *type) {
struct __pyx_typeinfo_string result = { {0} };
char *buf = (char *) result.string;
size_t size = type->size;
switch (type->typegroup) {
case 'I':
case 'U':
if (size == 1)
*buf = 'c';
else if (size == 2)
*buf = 'h';
else if (size == 4)
*buf = 'i';
else if (size == 8)
*buf = 'q';
if (type->is_unsigned)
*buf = toupper(*buf);
break;
case 'P':
*buf = 'P';
break;
case 'C':
{
__Pyx_TypeInfo complex_type = *type;
complex_type.typegroup = 'R';
complex_type.size /= 2;
*buf++ = 'Z';
/* Note: What about short/int/long complex? Probably not used? */
*buf = __Pyx_TypeInfoToFormat(&complex_type).string[0];
break;
}
case 'R':
if (size == 4)
*buf = 'f';
else if (size == 8)
*buf = 'd';
else
*buf = 'g';
break;
}
return result;
}
This diff is collapsed.
...@@ -56,9 +56,8 @@ static CYTHON_INLINE char *__pyx_memviewslice_index_full(const char *bufp, Py_ss ...@@ -56,9 +56,8 @@ static CYTHON_INLINE char *__pyx_memviewslice_index_full(const char *bufp, Py_ss
{{#__Pyx_PyObject_to_MemoryviewSlice_<count>}} {{#__Pyx_PyObject_to_MemoryviewSlice_<count>}}
static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *obj) { static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *obj) {
{{memviewslice_name}} result; {{memviewslice_name}} result = {0};
result.memview = NULL;
result.data = NULL;
struct __pyx_memoryview_obj *memview = \ struct __pyx_memoryview_obj *memview = \
(struct __pyx_memoryview_obj *) __pyx_memoryview_new(obj, {{buf_flag}}); (struct __pyx_memoryview_obj *) __pyx_memoryview_new(obj, {{buf_flag}});
__Pyx_BufFmt_StackElem stack[{{struct_nesting_depth}}]; __Pyx_BufFmt_StackElem stack[{{struct_nesting_depth}}];
...@@ -294,9 +293,9 @@ static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice, ...@@ -294,9 +293,9 @@ static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice,
__pyx_fatalerror("Acquisition count is %d (line %d)", __pyx_fatalerror("Acquisition count is %d (line %d)",
memview->acquisition_count, lineno); memview->acquisition_count, lineno);
//PyThread_acquire_lock(memview->lock, 1); PyThread_acquire_lock(memview->lock, 1);
first_time = (memview->acquisition_count++ == 0); first_time = (memview->acquisition_count++ == 0);
//PyThread_release_lock(memview->lock); PyThread_release_lock(memview->lock);
if (first_time) { if (first_time) {
if (have_gil) { if (have_gil) {
...@@ -321,9 +320,9 @@ static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice, ...@@ -321,9 +320,9 @@ static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice,
__pyx_fatalerror("Acquisition count is %d (line %d)", __pyx_fatalerror("Acquisition count is %d (line %d)",
memview->acquisition_count, lineno); memview->acquisition_count, lineno);
//PyThread_acquire_lock(memview->lock, 1); PyThread_acquire_lock(memview->lock, 1);
last_time = (memview->acquisition_count-- == 1); last_time = (memview->acquisition_count-- == 1);
//PyThread_release_lock(memview->lock); PyThread_release_lock(memview->lock);
if (last_time) { if (last_time) {
if (have_gil) { if (have_gil) {
...@@ -370,7 +369,7 @@ static __Pyx_memviewslice {{copy_name}}(const __Pyx_memviewslice from_mvs) { ...@@ -370,7 +369,7 @@ static __Pyx_memviewslice {{copy_name}}(const __Pyx_memviewslice from_mvs) {
} }
} }
array_obj = __pyx_array_new(shape_tuple, {{sizeof_dtype}}, buf->format, mode); array_obj = __pyx_array_new(shape_tuple, {{sizeof_dtype}}, buf->format, mode, NULL);
if (unlikely(!array_obj)) { if (unlikely(!array_obj)) {
goto fail; goto fail;
} }
...@@ -412,12 +411,15 @@ no_fail: ...@@ -412,12 +411,15 @@ no_fail:
/////////////// MemviewSliceIndex /////////////// /////////////// MemviewSliceIndex ///////////////
static CYTHON_INLINE char *__pyx_memviewslice_index_full(const char *bufp, Py_ssize_t idx, Py_ssize_t stride, Py_ssize_t suboffset) { static CYTHON_INLINE char *
__pyx_memviewslice_index_full(const char *bufp, Py_ssize_t idx,
Py_ssize_t stride, Py_ssize_t suboffset)
{
bufp = bufp + idx * stride; bufp = bufp + idx * stride;
if (suboffset >= 0) { if (suboffset >= 0) {
bufp = *((char **) bufp) + suboffset; bufp = *((char **) bufp) + suboffset;
} }
return bufp; return (char *) bufp;
} }
/////////////// MemviewDtypeToObject.proto /////////////// /////////////// MemviewDtypeToObject.proto ///////////////
......
...@@ -5,6 +5,8 @@ from __future__ import unicode_literals ...@@ -5,6 +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
def contiguity(): def contiguity():
''' '''
>>> contiguity() >>> contiguity()
...@@ -85,19 +87,79 @@ cdef create_array(shape, mode): ...@@ -85,19 +87,79 @@ cdef create_array(shape, mode):
return result return result
def test_cython_array(): def test_cython_array_getbuffer():
""" """
>>> test_cython_array() >>> test_cython_array_getbuffer()
98 98
61 61
98 98
61 61
""" """
cdef int[:, ::1] carr = create_array((14, 10), 'c') cdef int[:, ::1] cslice = create_array((14, 10), 'c')
cdef int[::1, :] farr = create_array((14, 10), 'fortran') cdef int[::1, :] fslice = create_array((14, 10), 'fortran')
print cslice[9, 8]
print cslice[6, 1]
print fslice[9, 8]
print fslice[6, 1]
def test_cython_array_index():
"""
>>> test_cython_array_index()
98
61
98
61
"""
c_array = create_array((14, 10), 'c')
f_array = create_array((14, 10), 'fortran')
print c_array[9, 8]
print c_array[6, 1]
print f_array[9, 8]
print f_array[6, 1]
cdef int *getp(int dim1=10, int dim2=10) except NULL:
print "getp()"
cdef int *p = <int *> malloc(dim1 * dim2 * sizeof(int))
if p == NULL:
raise MemoryError
for i in range(dim1 * dim2):
p[i] = i
return p
cdef void callback_free_data(char *p):
print 'callback free data called'
free(p)
def test_array_from_pointer():
"""
>>> test_array_from_pointer()
getp()
69
c
getp()
fortran
getp()
56
getp()
56
callback free data called
"""
cdef int *p = getp()
cdef array c_arr = <int[:10, :10]> p
c_arr.callback_free_data = callback_free_data
print c_arr[6, 9]
print c_arr.mode
print carr[9, 8] print (<int[:10:1, :10]> getp()).mode
print carr[6, 1]
print farr[9, 8] cdef int[:, ::1] mslice = <int[:10, :10]> getp()
print farr[6, 1] print mslice[5, 6]
print (<int[:12, :10]> getp(12, 10))[5, 6]
...@@ -219,28 +219,20 @@ def get_int_2d(int[:, :] mslice, int i, int j): ...@@ -219,28 +219,20 @@ def get_int_2d(int[:, :] mslice, int i, int j):
>>> C = IntMockBuffer("C", range(6), (2,3)) >>> C = IntMockBuffer("C", range(6), (2,3))
>>> get_int_2d(C, 1, 1) >>> get_int_2d(C, 1, 1)
acquired C acquired C
acquired C
released C
released C released C
4 4
Check negative indexing: Check negative indexing:
>>> get_int_2d(C, -1, 0) >>> get_int_2d(C, -1, 0)
acquired C acquired C
acquired C
released C
released C released C
3 3
>>> get_int_2d(C, -1, -2) >>> get_int_2d(C, -1, -2)
acquired C acquired C
acquired C
released C
released C released C
4 4
>>> get_int_2d(C, -2, -3) >>> get_int_2d(C, -2, -3)
acquired C acquired C
acquired C
released C
released C released C
0 0
...@@ -265,50 +257,34 @@ def set_int_2d(int[:, :] mslice, int i, int j, int value): ...@@ -265,50 +257,34 @@ def set_int_2d(int[:, :] mslice, int i, int j, int value):
>>> C = IntMockBuffer("C", range(6), (2,3)) >>> C = IntMockBuffer("C", range(6), (2,3))
>>> set_int_2d(C, 1, 1, 10) >>> set_int_2d(C, 1, 1, 10)
acquired C acquired C
acquired C
released C
released C released C
>>> get_int_2d(C, 1, 1) >>> get_int_2d(C, 1, 1)
acquired C acquired C
acquired C
released C
released C released C
10 10
Check negative indexing: Check negative indexing:
>>> set_int_2d(C, -1, 0, 3) >>> set_int_2d(C, -1, 0, 3)
acquired C acquired C
acquired C
released C
released C released C
>>> get_int_2d(C, -1, 0) >>> get_int_2d(C, -1, 0)
acquired C acquired C
acquired C
released C
released C released C
3 3
>>> set_int_2d(C, -1, -2, 8) >>> set_int_2d(C, -1, -2, 8)
acquired C acquired C
acquired C
released C
released C released C
>>> get_int_2d(C, -1, -2) >>> get_int_2d(C, -1, -2)
acquired C acquired C
acquired C
released C
released C released C
8 8
>>> set_int_2d(C, -2, -3, 9) >>> set_int_2d(C, -2, -3, 9)
acquired C acquired C
acquired C
released C
released C released C
>>> get_int_2d(C, -2, -3) >>> get_int_2d(C, -2, -3)
acquired C acquired C
acquired C
released C
released C released C
9 9
...@@ -336,8 +312,6 @@ def writable(unsigned short int[:, :, :] mslice): ...@@ -336,8 +312,6 @@ def writable(unsigned short int[:, :, :] mslice):
>>> R = UnsignedShortMockBuffer("R", range(27), shape=(3, 3, 3)) >>> R = UnsignedShortMockBuffer("R", range(27), shape=(3, 3, 3))
>>> writable(R) >>> writable(R)
acquired R acquired R
acquired R
released R
released R released R
>>> [str(x) for x in R.recieved_flags] # Py2/3 >>> [str(x) for x in R.recieved_flags] # Py2/3
['FORMAT', 'ND', 'STRIDES', 'WRITABLE'] ['FORMAT', 'ND', 'STRIDES', 'WRITABLE']
...@@ -350,8 +324,6 @@ def strided(int[:] mslice): ...@@ -350,8 +324,6 @@ def strided(int[:] mslice):
>>> A = IntMockBuffer("A", range(4)) >>> A = IntMockBuffer("A", range(4))
>>> strided(A) >>> strided(A)
acquired A acquired A
acquired A
released A
released A released A
2 2
...@@ -410,16 +382,12 @@ def generic(int[::view.generic, ::view.generic] mslice1, ...@@ -410,16 +382,12 @@ def generic(int[::view.generic, ::view.generic] mslice1,
>>> generic(A, B) >>> generic(A, B)
acquired A acquired A
acquired B acquired B
acquired A
acquired B
4 4
4 4
10 10
11 11
released A released A
released B released B
released A
released B
""" """
buf1, buf2 = mslice1, mslice2 buf1, buf2 = mslice1, mslice2
...@@ -440,16 +408,12 @@ def generic_contig(int[::view.generic_contiguous, :] mslice1, ...@@ -440,16 +408,12 @@ def generic_contig(int[::view.generic_contiguous, :] mslice1,
>>> generic_contig(A, B) >>> generic_contig(A, B)
acquired A acquired A
acquired B acquired B
acquired A
acquired B
4 4
4 4
10 10
11 11
released A released A
released B released B
released A
released B
""" """
buf1, buf2 = mslice1, mslice2 buf1, buf2 = mslice1, mslice2
......
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