#
# Analysis
#
-buffer_options = ("dtype", "ndim", "mode") # ordered!
-buffer_defaults = {"ndim": 1, "mode": "full"}
+buffer_options = ("dtype", "ndim", "mode", "negative_indices") # ordered!
+buffer_defaults = {"ndim": 1, "mode": "full", "negative_indices": True}
buffer_positional_options_count = 1 # anything beyond this needs keyword argument
ERR_BUF_OPTION_UNKNOWN = '"%s" is not a buffer option'
ERR_BUF_MODE = 'Only allowed buffer modes are: "c", "fortran", "full", "strided" (as a compile-time string)'
ERR_BUF_NDIM = 'ndim must be a non-negative integer'
ERR_BUF_DTYPE = 'dtype must be "object", numeric type or a struct'
+ERR_BUF_NEGATIVE_INDICES = 'negative_indices must be a boolean'
def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, need_complete=True):
"""
if mode and not (mode in ('full', 'strided', 'c', 'fortran')):
raise CompileError(globalpos, ERR_BUF_MODE)
+ negative_indices = options.get("negative_indices")
+ if mode and not isinstance(negative_indices, bool):
+ raise CompileError(globalpos, ERR_BUF_NEGATIVE_INDICES)
+
return options
"""
bufaux = entry.buffer_aux
bufstruct = bufaux.buffer_info_var.cname
+ negative_indices = entry.type.negative_indices
if options['boundscheck']:
# Check bounds and fix negative indices.
if signed != 0:
# not unsigned, deal with negative index
code.putln("if (%s < 0) {" % cname)
- code.putln("%s += %s;" % (cname, shape.cname))
- code.putln("if (%s) %s = %d;" % (
- code.unlikely("%s < 0" % cname), tmp_cname, dim))
+ if negative_indices:
+ code.putln("%s += %s;" % (cname, shape.cname))
+ code.putln("if (%s) %s = %d;" % (
+ code.unlikely("%s < 0" % cname), tmp_cname, dim))
+ else:
+ code.putln("%s = %d;" % (tmp_cname, dim))
code.put("} else ")
# check bounds in positive direction
code.putln("if (%s) %s = %d;" % (
code.putln(code.error_goto(pos))
code.end_block()
code.funcstate.release_temp(tmp_cname)
- else:
+ elif negative_indices:
# Only fix negative indices.
for signed, cname, shape in zip(index_signeds, index_cnames,
bufaux.shapevars):
cdef int i
print u"|".join([unicode(buf[i]) for i in range(len)])
+#
+# The negative_indices buffer option
+#
+@testcase
+def no_negative_indices(object[int, negative_indices=False] buf, int idx):
+ """
+ The most interesting thing here is to inspect the C source and
+ make sure optimal code is produced.
+
+ >>> A = IntMockBuffer(None, range(6))
+ >>> no_negative_indices(A, 3)
+ 3
+ >>> no_negative_indices(A, -1)
+ Traceback (most recent call last):
+ ...
+ IndexError: Out of bounds on buffer access (axis 0)
+ """
+ return buf[idx]
+
#
# Buffer type mismatch examples. Varying the type and access
# method simultaneously, the odds of an interaction is virtually
return buf[idx]
@testcase
-@cython.boundscheck(False)
+@cython.boundscheck(False) # outer decorators should take precedence
@cython.boundscheck(True)
def unsafe_get(object[int] buf, int idx):
"""
"""
return buf[idx]
+@testcase
+@cython.boundscheck(False)
+def unsafe_get_nonegative(object[int, negative_indices=False] buf, int idx):
+ """
+ Also inspect the C source to see that it is optimal...
+
+ >>> A = IntMockBuffer(None, range(10), shape=(3,), offset=5)
+ >>> unsafe_get_nonegative(A, -2)
+ 3
+ """
+ return buf[idx]
+
@testcase
def mixed_get(object[int] buf, int unsafe_idx, int safe_idx):
"""
cdef get_itemsize(self): return sizeof(int)
cdef get_default_format(self): return b"@i"
+cdef class UnsignedIntMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ (<unsigned int*>buf)[0] = <unsigned int>value
+ return 0
+ cdef get_itemsize(self): return sizeof(unsigned int)
+ cdef get_default_format(self): return b"@I"
+
cdef class ShortMockBuffer(MockBuffer):
cdef int write(self, char* buf, object value) except -1:
(<short*>buf)[0] = <short>value