1from __future__ import absolute_import
2
3from .Visitor import CythonTransform
4from .ModuleNode import ModuleNode
5from .Errors import CompileError
6from .UtilityCode import CythonUtilityCode
7from .Code import UtilityCode, TempitaUtilityCode
8
9from . import Options
10from . import Interpreter
11from . import PyrexTypes
12from . import Naming
13from . import Symtab
14
15def dedent(text, reindent=0):
16    from textwrap import dedent
17    text = dedent(text)
18    if reindent > 0:
19        indent = " " * reindent
20        text = '\n'.join([indent + x for x in text.split('\n')])
21    return text
22
23class IntroduceBufferAuxiliaryVars(CythonTransform):
24
25    #
26    # Entry point
27    #
28
29    buffers_exists = False
30    using_memoryview = False
31
32    def __call__(self, node):
33        assert isinstance(node, ModuleNode)
34        self.max_ndim = 0
35        result = super(IntroduceBufferAuxiliaryVars, self).__call__(node)
36        if self.buffers_exists:
37            use_bufstruct_declare_code(node.scope)
38            use_py2_buffer_functions(node.scope)
39
40        return result
41
42
43    #
44    # Basic operations for transforms
45    #
46    def handle_scope(self, node, scope):
47        # For all buffers, insert extra variables in the scope.
48        # The variables are also accessible from the buffer_info
49        # on the buffer entry
50        scope_items = scope.entries.items()
51        bufvars = [entry for name, entry in scope_items if entry.type.is_buffer]
52        if len(bufvars) > 0:
53            bufvars.sort(key=lambda entry: entry.name)
54            self.buffers_exists = True
55
56        memviewslicevars = [entry for name, entry in scope_items if entry.type.is_memoryviewslice]
57        if len(memviewslicevars) > 0:
58            self.buffers_exists = True
59
60
61        for (name, entry) in scope_items:
62            if name == 'memoryview' and isinstance(entry.utility_code_definition, CythonUtilityCode):
63                self.using_memoryview = True
64                break
65        del scope_items
66
67        if isinstance(node, ModuleNode) and len(bufvars) > 0:
68            # for now...note that pos is wrong
69            raise CompileError(node.pos, "Buffer vars not allowed in module scope")
70        for entry in bufvars:
71            if entry.type.dtype.is_ptr:
72                raise CompileError(node.pos, "Buffers with pointer types not yet supported.")
73
74            name = entry.name
75            buftype = entry.type
76            if buftype.ndim > Options.buffer_max_dims:
77                raise CompileError(node.pos,
78                        "Buffer ndims exceeds Options.buffer_max_dims = %d" % Options.buffer_max_dims)
79            if buftype.ndim > self.max_ndim:
80                self.max_ndim = buftype.ndim
81
82            # Declare auxiliary vars
83            def decvar(type, prefix):
84                cname = scope.mangle(prefix, name)
85                aux_var = scope.declare_var(name=None, cname=cname,
86                                            type=type, pos=node.pos)
87                if entry.is_arg:
88                    aux_var.used = True # otherwise, NameNode will mark whether it is used
89
90                return aux_var
91
92            auxvars = ((PyrexTypes.c_pyx_buffer_nd_type, Naming.pybuffernd_prefix),
93                       (PyrexTypes.c_pyx_buffer_type, Naming.pybufferstruct_prefix))
94            pybuffernd, rcbuffer = [decvar(type, prefix) for (type, prefix) in auxvars]
95
96            entry.buffer_aux = Symtab.BufferAux(pybuffernd, rcbuffer)
97
98        scope.buffer_entries = bufvars
99        self.scope = scope
100
101    def visit_ModuleNode(self, node):
102        self.handle_scope(node, node.scope)
103        self.visitchildren(node)
104        return node
105
106    def visit_FuncDefNode(self, node):
107        self.handle_scope(node, node.local_scope)
108        self.visitchildren(node)
109        return node
110
111#
112# Analysis
113#
114buffer_options = ("dtype", "ndim", "mode", "negative_indices", "cast") # ordered!
115buffer_defaults = {"ndim": 1, "mode": "full", "negative_indices": True, "cast": False}
116buffer_positional_options_count = 1 # anything beyond this needs keyword argument
117
118ERR_BUF_OPTION_UNKNOWN = '"%s" is not a buffer option'
119ERR_BUF_TOO_MANY = 'Too many buffer options'
120ERR_BUF_DUP = '"%s" buffer option already supplied'
121ERR_BUF_MISSING = '"%s" missing'
122ERR_BUF_MODE = 'Only allowed buffer modes are: "c", "fortran", "full", "strided" (as a compile-time string)'
123ERR_BUF_NDIM = 'ndim must be a non-negative integer'
124ERR_BUF_DTYPE = 'dtype must be "object", numeric type or a struct'
125ERR_BUF_BOOL = '"%s" must be a boolean'
126
127def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, need_complete=True):
128    """
129    Must be called during type analysis, as analyse is called
130    on the dtype argument.
131
132    posargs and dictargs should consist of a list and a dict
133    of tuples (value, pos). Defaults should be a dict of values.
134
135    Returns a dict containing all the options a buffer can have and
136    its value (with the positions stripped).
137    """
138    if defaults is None:
139        defaults = buffer_defaults
140
141    posargs, dictargs = Interpreter.interpret_compiletime_options(
142        posargs, dictargs, type_env=env, type_args=(0, 'dtype'))
143
144    if len(posargs) > buffer_positional_options_count:
145        raise CompileError(posargs[-1][1], ERR_BUF_TOO_MANY)
146
147    options = {}
148    for name, (value, pos) in dictargs.items():
149        if not name in buffer_options:
150            raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name)
151        options[name] = value
152
153    for name, (value, pos) in zip(buffer_options, posargs):
154        if not name in buffer_options:
155            raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name)
156        if name in options:
157            raise CompileError(pos, ERR_BUF_DUP % name)
158        options[name] = value
159
160    # Check that they are all there and copy defaults
161    for name in buffer_options:
162        if not name in options:
163            try:
164                options[name] = defaults[name]
165            except KeyError:
166                if need_complete:
167                    raise CompileError(globalpos, ERR_BUF_MISSING % name)
168
169    dtype = options.get("dtype")
170    if dtype and dtype.is_extension_type:
171        raise CompileError(globalpos, ERR_BUF_DTYPE)
172
173    ndim = options.get("ndim")
174    if ndim and (not isinstance(ndim, int) or ndim < 0):
175        raise CompileError(globalpos, ERR_BUF_NDIM)
176
177    mode = options.get("mode")
178    if mode and not (mode in ('full', 'strided', 'c', 'fortran')):
179        raise CompileError(globalpos, ERR_BUF_MODE)
180
181    def assert_bool(name):
182        x = options.get(name)
183        if not isinstance(x, bool):
184            raise CompileError(globalpos, ERR_BUF_BOOL % name)
185
186    assert_bool('negative_indices')
187    assert_bool('cast')
188
189    return options
190
191
192#
193# Code generation
194#
195
196class BufferEntry(object):
197    def __init__(self, entry):
198        self.entry = entry
199        self.type = entry.type
200        self.cname = entry.buffer_aux.buflocal_nd_var.cname
201        self.buf_ptr = "%s.rcbuffer->pybuffer.buf" % self.cname
202        self.buf_ptr_type = entry.type.buffer_ptr_type
203        self.init_attributes()
204
205    def init_attributes(self):
206        self.shape = self.get_buf_shapevars()
207        self.strides = self.get_buf_stridevars()
208        self.suboffsets = self.get_buf_suboffsetvars()
209
210    def get_buf_suboffsetvars(self):
211        return self._for_all_ndim("%s.diminfo[%d].suboffsets")
212
213    def get_buf_stridevars(self):
214        return self._for_all_ndim("%s.diminfo[%d].strides")
215
216    def get_buf_shapevars(self):
217        return self._for_all_ndim("%s.diminfo[%d].shape")
218
219    def _for_all_ndim(self, s):
220        return [s % (self.cname, i) for i in range(self.type.ndim)]
221
222    def generate_buffer_lookup_code(self, code, index_cnames):
223        # Create buffer lookup and return it
224        # This is done via utility macros/inline functions, which vary
225        # according to the access mode used.
226        params = []
227        nd = self.type.ndim
228        mode = self.type.mode
229        if mode == 'full':
230            for i, s, o in zip(index_cnames,
231                               self.get_buf_stridevars(),
232                               self.get_buf_suboffsetvars()):
233                params.append(i)
234                params.append(s)
235                params.append(o)
236            funcname = "__Pyx_BufPtrFull%dd" % nd
237            funcgen = buf_lookup_full_code
238        else:
239            if mode == 'strided':
240                funcname = "__Pyx_BufPtrStrided%dd" % nd
241                funcgen = buf_lookup_strided_code
242            elif mode == 'c':
243                funcname = "__Pyx_BufPtrCContig%dd" % nd
244                funcgen = buf_lookup_c_code
245            elif mode == 'fortran':
246                funcname = "__Pyx_BufPtrFortranContig%dd" % nd
247                funcgen = buf_lookup_fortran_code
248            else:
249                assert False
250            for i, s in zip(index_cnames, self.get_buf_stridevars()):
251                params.append(i)
252                params.append(s)
253
254        # Make sure the utility code is available
255        if funcname not in code.globalstate.utility_codes:
256            code.globalstate.utility_codes.add(funcname)
257            protocode = code.globalstate['utility_code_proto']
258            defcode = code.globalstate['utility_code_def']
259            funcgen(protocode, defcode, name=funcname, nd=nd)
260
261        buf_ptr_type_code = self.buf_ptr_type.empty_declaration_code()
262        ptrcode = "%s(%s, %s, %s)" % (funcname, buf_ptr_type_code, self.buf_ptr,
263                                      ", ".join(params))
264        return ptrcode
265
266
267def get_flags(buffer_aux, buffer_type):
268    flags = 'PyBUF_FORMAT'
269    mode = buffer_type.mode
270    if mode == 'full':
271        flags += '| PyBUF_INDIRECT'
272    elif mode == 'strided':
273        flags += '| PyBUF_STRIDES'
274    elif mode == 'c':
275        flags += '| PyBUF_C_CONTIGUOUS'
276    elif mode == 'fortran':
277        flags += '| PyBUF_F_CONTIGUOUS'
278    else:
279        assert False
280    if buffer_aux.writable_needed: flags += "| PyBUF_WRITABLE"
281    return flags
282
283def used_buffer_aux_vars(entry):
284    buffer_aux = entry.buffer_aux
285    buffer_aux.buflocal_nd_var.used = True
286    buffer_aux.rcbuf_var.used = True
287
288def put_unpack_buffer_aux_into_scope(buf_entry, code):
289    # Generate code to copy the needed struct info into local
290    # variables.
291    buffer_aux, mode = buf_entry.buffer_aux, buf_entry.type.mode
292    pybuffernd_struct = buffer_aux.buflocal_nd_var.cname
293
294    fldnames = ['strides', 'shape']
295    if mode == 'full':
296        fldnames.append('suboffsets')
297
298    ln = []
299    for i in range(buf_entry.type.ndim):
300        for fldname in fldnames:
301            ln.append("%s.diminfo[%d].%s = %s.rcbuffer->pybuffer.%s[%d];" % \
302                    (pybuffernd_struct, i, fldname,
303                     pybuffernd_struct, fldname, i))
304    code.putln(' '.join(ln))
305
306def put_init_vars(entry, code):
307    bufaux = entry.buffer_aux
308    pybuffernd_struct = bufaux.buflocal_nd_var.cname
309    pybuffer_struct = bufaux.rcbuf_var.cname
310    # init pybuffer_struct
311    code.putln("%s.pybuffer.buf = NULL;" % pybuffer_struct)
312    code.putln("%s.refcount = 0;" % pybuffer_struct)
313    # init the buffer object
314    # code.put_init_var_to_py_none(entry)
315    # init the pybuffernd_struct
316    code.putln("%s.data = NULL;" % pybuffernd_struct)
317    code.putln("%s.rcbuffer = &%s;" % (pybuffernd_struct, pybuffer_struct))
318
319
320def put_acquire_arg_buffer(entry, code, pos):
321    buffer_aux = entry.buffer_aux
322    getbuffer = get_getbuffer_call(code, entry.cname, buffer_aux, entry.type)
323
324    # Acquire any new buffer
325    code.putln("{")
326    code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % entry.type.dtype.struct_nesting_depth())
327    code.putln(code.error_goto_if("%s == -1" % getbuffer, pos))
328    code.putln("}")
329    # An exception raised in arg parsing cannot be caught, so no
330    # need to care about the buffer then.
331    put_unpack_buffer_aux_into_scope(entry, code)
332
333
334def put_release_buffer_code(code, entry):
335    code.globalstate.use_utility_code(acquire_utility_code)
336    code.putln("__Pyx_SafeReleaseBuffer(&%s.rcbuffer->pybuffer);" % entry.buffer_aux.buflocal_nd_var.cname)
337
338
339def get_getbuffer_call(code, obj_cname, buffer_aux, buffer_type):
340    ndim = buffer_type.ndim
341    cast = int(buffer_type.cast)
342    flags = get_flags(buffer_aux, buffer_type)
343    pybuffernd_struct = buffer_aux.buflocal_nd_var.cname
344
345    dtype_typeinfo = get_type_information_cname(code, buffer_type.dtype)
346
347    code.globalstate.use_utility_code(acquire_utility_code)
348    return ("__Pyx_GetBufferAndValidate(&%(pybuffernd_struct)s.rcbuffer->pybuffer, "
349            "(PyObject*)%(obj_cname)s, &%(dtype_typeinfo)s, %(flags)s, %(ndim)d, "
350            "%(cast)d, __pyx_stack)" % locals())
351
352
353def put_assign_to_buffer(lhs_cname, rhs_cname, buf_entry,
354                         is_initialized, pos, code):
355    """
356    Generate code for reassigning a buffer variables. This only deals with getting
357    the buffer auxiliary structure and variables set up correctly, the assignment
358    itself and refcounting is the responsibility of the caller.
359
360    However, the assignment operation may throw an exception so that the reassignment
361    never happens.
362
363    Depending on the circumstances there are two possible outcomes:
364    - Old buffer released, new acquired, rhs assigned to lhs
365    - Old buffer released, new acquired which fails, reaqcuire old lhs buffer
366      (which may or may not succeed).
367    """
368
369    buffer_aux, buffer_type = buf_entry.buffer_aux, buf_entry.type
370    pybuffernd_struct = buffer_aux.buflocal_nd_var.cname
371    flags = get_flags(buffer_aux, buffer_type)
372
373    code.putln("{")  # Set up necessary stack for getbuffer
374    code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % buffer_type.dtype.struct_nesting_depth())
375
376    getbuffer = get_getbuffer_call(code, "%s", buffer_aux, buffer_type) # fill in object below
377
378    if is_initialized:
379        # Release any existing buffer
380        code.putln('__Pyx_SafeReleaseBuffer(&%s.rcbuffer->pybuffer);' % pybuffernd_struct)
381        # Acquire
382        retcode_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
383        code.putln("%s = %s;" % (retcode_cname, getbuffer % rhs_cname))
384        code.putln('if (%s) {' % (code.unlikely("%s < 0" % retcode_cname)))
385        # If acquisition failed, attempt to reacquire the old buffer
386        # before raising the exception. A failure of reacquisition
387        # will cause the reacquisition exception to be reported, one
388        # can consider working around this later.
389        exc_temps = tuple(code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=False)
390                          for _ in range(3))
391        code.putln('PyErr_Fetch(&%s, &%s, &%s);' % exc_temps)
392        code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % lhs_cname)))
393        code.putln('Py_XDECREF(%s); Py_XDECREF(%s); Py_XDECREF(%s);' % exc_temps)  # Do not refnanny these!
394        code.globalstate.use_utility_code(raise_buffer_fallback_code)
395        code.putln('__Pyx_RaiseBufferFallbackError();')
396        code.putln('} else {')
397        code.putln('PyErr_Restore(%s, %s, %s);' % exc_temps)
398        code.putln('}')
399        code.putln('%s = %s = %s = 0;' % exc_temps)
400        for t in exc_temps:
401            code.funcstate.release_temp(t)
402        code.putln('}')
403        # Unpack indices
404        put_unpack_buffer_aux_into_scope(buf_entry, code)
405        code.putln(code.error_goto_if_neg(retcode_cname, pos))
406        code.funcstate.release_temp(retcode_cname)
407    else:
408        # Our entry had no previous value, so set to None when acquisition fails.
409        # In this case, auxiliary vars should be set up right in initialization to a zero-buffer,
410        # so it suffices to set the buf field to NULL.
411        code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % rhs_cname)))
412        code.putln('%s = %s; __Pyx_INCREF(Py_None); %s.rcbuffer->pybuffer.buf = NULL;' %
413                   (lhs_cname,
414                    PyrexTypes.typecast(buffer_type, PyrexTypes.py_object_type, "Py_None"),
415                    pybuffernd_struct))
416        code.putln(code.error_goto(pos))
417        code.put('} else {')
418        # Unpack indices
419        put_unpack_buffer_aux_into_scope(buf_entry, code)
420        code.putln('}')
421
422    code.putln("}") # Release stack
423
424
425def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives,
426                           pos, code, negative_indices, in_nogil_context):
427    """
428    Generates code to process indices and calculate an offset into
429    a buffer. Returns a C string which gives a pointer which can be
430    read from or written to at will (it is an expression so caller should
431    store it in a temporary if it is used more than once).
432
433    As the bounds checking can have any number of combinations of unsigned
434    arguments, smart optimizations etc. we insert it directly in the function
435    body. The lookup however is delegated to a inline function that is instantiated
436    once per ndim (lookup with suboffsets tend to get quite complicated).
437
438    entry is a BufferEntry
439    """
440    negative_indices = directives['wraparound'] and negative_indices
441
442    if directives['boundscheck']:
443        # Check bounds and fix negative indices.
444        # We allocate a temporary which is initialized to -1, meaning OK (!).
445        # If an error occurs, the temp is set to the index dimension the
446        # error is occurring at.
447        failed_dim_temp = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
448        code.putln("%s = -1;" % failed_dim_temp)
449        for dim, (signed, cname, shape) in enumerate(zip(index_signeds, index_cnames, entry.get_buf_shapevars())):
450            if signed != 0:
451                # not unsigned, deal with negative index
452                code.putln("if (%s < 0) {" % cname)
453                if negative_indices:
454                    code.putln("%s += %s;" % (cname, shape))
455                    code.putln("if (%s) %s = %d;" % (
456                        code.unlikely("%s < 0" % cname),
457                        failed_dim_temp, dim))
458                else:
459                    code.putln("%s = %d;" % (failed_dim_temp, dim))
460                code.put("} else ")
461            # check bounds in positive direction
462            if signed != 0:
463                cast = ""
464            else:
465                cast = "(size_t)"
466            code.putln("if (%s) %s = %d;" % (
467                code.unlikely("%s >= %s%s" % (cname, cast, shape)),
468                failed_dim_temp, dim))
469
470        if in_nogil_context:
471            code.globalstate.use_utility_code(raise_indexerror_nogil)
472            func = '__Pyx_RaiseBufferIndexErrorNogil'
473        else:
474            code.globalstate.use_utility_code(raise_indexerror_code)
475            func = '__Pyx_RaiseBufferIndexError'
476
477        code.putln("if (%s) {" % code.unlikely("%s != -1" % failed_dim_temp))
478        code.putln('%s(%s);' % (func, failed_dim_temp))
479        code.putln(code.error_goto(pos))
480        code.putln('}')
481        code.funcstate.release_temp(failed_dim_temp)
482    elif negative_indices:
483        # Only fix negative indices.
484        for signed, cname, shape in zip(index_signeds, index_cnames, entry.get_buf_shapevars()):
485            if signed != 0:
486                code.putln("if (%s < 0) %s += %s;" % (cname, cname, shape))
487
488    return entry.generate_buffer_lookup_code(code, index_cnames)
489
490
491def use_bufstruct_declare_code(env):
492    env.use_utility_code(buffer_struct_declare_code)
493
494
495def buf_lookup_full_code(proto, defin, name, nd):
496    """
497    Generates a buffer lookup function for the right number
498    of dimensions. The function gives back a void* at the right location.
499    """
500    # _i_ndex, _s_tride, sub_o_ffset
501    macroargs = ", ".join(["i%d, s%d, o%d" % (i, i, i) for i in range(nd)])
502    proto.putln("#define %s(type, buf, %s) (type)(%s_imp(buf, %s))" % (name, macroargs, name, macroargs))
503
504    funcargs = ", ".join(["Py_ssize_t i%d, Py_ssize_t s%d, Py_ssize_t o%d" % (i, i, i) for i in range(nd)])
505    proto.putln("static CYTHON_INLINE void* %s_imp(void* buf, %s);" % (name, funcargs))
506    defin.putln(dedent("""
507        static CYTHON_INLINE void* %s_imp(void* buf, %s) {
508          char* ptr = (char*)buf;
509        """) % (name, funcargs) + "".join([dedent("""\
510          ptr += s%d * i%d;
511          if (o%d >= 0) ptr = *((char**)ptr) + o%d;
512        """) % (i, i, i, i) for i in range(nd)]
513        ) + "\nreturn ptr;\n}")
514
515
516def buf_lookup_strided_code(proto, defin, name, nd):
517    """
518    Generates a buffer lookup function for the right number
519    of dimensions. The function gives back a void* at the right location.
520    """
521    # _i_ndex, _s_tride
522    args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
523    offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd)])
524    proto.putln("#define %s(type, buf, %s) (type)((char*)buf + %s)" % (name, args, offset))
525
526
527def buf_lookup_c_code(proto, defin, name, nd):
528    """
529    Similar to strided lookup, but can assume that the last dimension
530    doesn't need a multiplication as long as.
531    Still we keep the same signature for now.
532    """
533    if nd == 1:
534        proto.putln("#define %s(type, buf, i0, s0) ((type)buf + i0)" % name)
535    else:
536        args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
537        offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd - 1)])
538        proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, nd - 1))
539
540
541def buf_lookup_fortran_code(proto, defin, name, nd):
542    """
543    Like C lookup, but the first index is optimized instead.
544    """
545    if nd == 1:
546        proto.putln("#define %s(type, buf, i0, s0) ((type)buf + i0)" % name)
547    else:
548        args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
549        offset = " + ".join(["i%d * s%d" % (i, i) for i in range(1, nd)])
550        proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, 0))
551
552
553def use_py2_buffer_functions(env):
554    env.use_utility_code(GetAndReleaseBufferUtilityCode())
555
556
557class GetAndReleaseBufferUtilityCode(object):
558    # Emulation of PyObject_GetBuffer and PyBuffer_Release for Python 2.
559    # For >= 2.6 we do double mode -- use the new buffer interface on objects
560    # which has the right tp_flags set, but emulation otherwise.
561
562    requires = None
563    is_cython_utility = False
564
565    def __init__(self):
566        pass
567
568    def __eq__(self, other):
569        return isinstance(other, GetAndReleaseBufferUtilityCode)
570
571    def __hash__(self):
572        return 24342342
573
574    def get_tree(self, **kwargs): pass
575
576    def put_code(self, output):
577        code = output['utility_code_def']
578        proto_code = output['utility_code_proto']
579        env = output.module_node.scope
580        cython_scope = env.context.cython_scope
581
582        # Search all types for __getbuffer__ overloads
583        types = []
584        visited_scopes = set()
585        def find_buffer_types(scope):
586            if scope in visited_scopes:
587                return
588            visited_scopes.add(scope)
589            for m in scope.cimported_modules:
590                find_buffer_types(m)
591            for e in scope.type_entries:
592                if isinstance(e.utility_code_definition, CythonUtilityCode):
593                    continue
594                t = e.type
595                if t.is_extension_type:
596                    if scope is cython_scope and not e.used:
597                        continue
598                    release = get = None
599                    for x in t.scope.pyfunc_entries:
600                        if x.name == u"__getbuffer__": get = x.func_cname
601                        elif x.name == u"__releasebuffer__": release = x.func_cname
602                    if get:
603                        types.append((t.typeptr_cname, get, release))
604
605        find_buffer_types(env)
606
607        util_code = TempitaUtilityCode.load(
608            "GetAndReleaseBuffer", from_file="Buffer.c",
609            context=dict(types=types))
610
611        proto = util_code.format_code(util_code.proto)
612        impl = util_code.format_code(
613            util_code.inject_string_constants(util_code.impl, output)[1])
614
615        proto_code.putln(proto)
616        code.putln(impl)
617
618
619def mangle_dtype_name(dtype):
620    # Use prefixes to separate user defined types from builtins
621    # (consider "typedef float unsigned_int")
622    if dtype.is_pyobject:
623        return "object"
624    elif dtype.is_ptr:
625        return "ptr"
626    else:
627        if dtype.is_typedef or dtype.is_struct_or_union:
628            prefix = "nn_"
629        else:
630            prefix = ""
631        return prefix + dtype.specialization_name()
632
633def get_type_information_cname(code, dtype, maxdepth=None):
634    """
635    Output the run-time type information (__Pyx_TypeInfo) for given dtype,
636    and return the name of the type info struct.
637
638    Structs with two floats of the same size are encoded as complex numbers.
639    One can separate between complex numbers declared as struct or with native
640    encoding by inspecting to see if the fields field of the type is
641    filled in.
642    """
643    namesuffix = mangle_dtype_name(dtype)
644    name = "__Pyx_TypeInfo_%s" % namesuffix
645    structinfo_name = "__Pyx_StructFields_%s" % namesuffix
646
647    if dtype.is_error: return "<error>"
648
649    # It's critical that walking the type info doesn't use more stack
650    # depth than dtype.struct_nesting_depth() returns, so use an assertion for this
651    if maxdepth is None: maxdepth = dtype.struct_nesting_depth()
652    if maxdepth <= 0:
653        assert False
654
655    if name not in code.globalstate.utility_codes:
656        code.globalstate.utility_codes.add(name)
657        typecode = code.globalstate['typeinfo']
658
659        arraysizes = []
660        if dtype.is_array:
661            while dtype.is_array:
662                arraysizes.append(dtype.size)
663                dtype = dtype.base_type
664
665        complex_possible = dtype.is_struct_or_union and dtype.can_be_complex()
666
667        declcode = dtype.empty_declaration_code()
668        if dtype.is_simple_buffer_dtype():
669            structinfo_name = "NULL"
670        elif dtype.is_struct:
671            struct_scope = dtype.scope
672            if dtype.is_const:
673                struct_scope = struct_scope.const_base_type_scope
674            # Must pre-call all used types in order not to recurse during utility code writing.
675            fields = struct_scope.var_entries
676            assert len(fields) > 0
677            types = [get_type_information_cname(code, f.type, maxdepth - 1)
678                     for f in fields]
679            typecode.putln("static __Pyx_StructField %s[] = {" % structinfo_name, safe=True)
680            for f, typeinfo in zip(fields, types):
681                typecode.putln('  {&%s, "%s", offsetof(%s, %s)},' %
682                           (typeinfo, f.name, dtype.empty_declaration_code(), f.cname), safe=True)
683            typecode.putln('  {NULL, NULL, 0}', safe=True)
684            typecode.putln("};", safe=True)
685        else:
686            assert False
687
688        rep = str(dtype)
689
690        flags = "0"
691        is_unsigned = "0"
692        if dtype is PyrexTypes.c_char_type:
693            is_unsigned = "IS_UNSIGNED(%s)" % declcode
694            typegroup = "'H'"
695        elif dtype.is_int:
696            is_unsigned = "IS_UNSIGNED(%s)" % declcode
697            typegroup = "%s ? 'U' : 'I'" % is_unsigned
698        elif complex_possible or dtype.is_complex:
699            typegroup = "'C'"
700        elif dtype.is_float:
701            typegroup = "'R'"
702        elif dtype.is_struct:
703            typegroup = "'S'"
704            if dtype.packed:
705                flags = "__PYX_BUF_FLAGS_PACKED_STRUCT"
706        elif dtype.is_pyobject:
707            typegroup = "'O'"
708        else:
709            assert False, dtype
710
711        typeinfo = ('static __Pyx_TypeInfo %s = '
712                        '{ "%s", %s, sizeof(%s), { %s }, %s, %s, %s, %s };')
713        tup = (name, rep, structinfo_name, declcode,
714               ', '.join([str(x) for x in arraysizes]) or '0', len(arraysizes),
715               typegroup, is_unsigned, flags)
716        typecode.putln(typeinfo % tup, safe=True)
717
718    return name
719
720def load_buffer_utility(util_code_name, context=None, **kwargs):
721    if context is None:
722        return UtilityCode.load(util_code_name, "Buffer.c", **kwargs)
723    else:
724        return TempitaUtilityCode.load(util_code_name, "Buffer.c", context=context, **kwargs)
725
726context = dict(max_dims=Options.buffer_max_dims)
727buffer_struct_declare_code = load_buffer_utility("BufferStructDeclare", context=context)
728buffer_formats_declare_code = load_buffer_utility("BufferFormatStructs")
729
730# Utility function to set the right exception
731# The caller should immediately goto_error
732raise_indexerror_code = load_buffer_utility("BufferIndexError")
733raise_indexerror_nogil = load_buffer_utility("BufferIndexErrorNogil")
734raise_buffer_fallback_code = load_buffer_utility("BufferFallbackError")
735
736acquire_utility_code = load_buffer_utility("BufferGetAndValidate", context=context)
737buffer_format_check_code = load_buffer_utility("BufferFormatCheck", context=context)
738
739# See utility code BufferFormatFromTypeInfo
740_typeinfo_to_format_code = load_buffer_utility("TypeInfoToFormat")
741