#
# Analysis
#
-buffer_options = ("dtype", "ndim", "mode", "negative_indices") # ordered!
-buffer_defaults = {"ndim": 1, "mode": "full", "negative_indices": True}
+buffer_options = ("dtype", "ndim", "mode", "negative_indices", "cast") # ordered!
+buffer_defaults = {"ndim": 1, "mode": "full", "negative_indices": True, "cast": False}
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'
+ERR_BUF_BOOL = '"%s" 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)
+ def assert_bool(name):
+ x = options.get(name)
+ if not isinstance(x, bool):
+ raise CompileError(globalpos, ERR_BUF_BOOL % name)
+
+ assert_bool('negative_indices')
+ assert_bool('cast')
return options
code.globalstate.use_utility_code(acquire_utility_code)
buffer_aux = entry.buffer_aux
getbuffer_cname = get_getbuffer_code(entry.type.dtype, code)
+
# Acquire any new buffer
- code.putln(code.error_goto_if("%s((PyObject*)%s, &%s, %s, %d) == -1" % (
+ code.putln(code.error_goto_if("%s((PyObject*)%s, &%s, %s, %d, %d) == -1" % (
getbuffer_cname,
entry.cname,
entry.buffer_aux.buffer_info_var.cname,
get_flags(buffer_aux, entry.type),
- entry.type.ndim), pos))
+ entry.type.ndim,
+ int(entry.type.cast)), pos))
# An exception raised in arg parsing cannot be catched, so no
# need to care about the buffer then.
put_unpack_buffer_aux_into_scope(buffer_aux, entry.type.mode, code)
bufstruct = buffer_aux.buffer_info_var.cname
flags = get_flags(buffer_aux, buffer_type)
- getbuffer = "%s((PyObject*)%%s, &%s, %s, %d)" % (get_getbuffer_code(buffer_type.dtype, code),
+ getbuffer = "%s((PyObject*)%%s, &%s, %s, %d, %d)" % (get_getbuffer_code(buffer_type.dtype, code),
# note: object is filled in later (%%s)
bufstruct,
flags,
- buffer_type.ndim)
+ buffer_type.ndim,
+ int(buffer_type.cast))
if is_initialized:
# Release any existing buffer
if not code.globalstate.has_utility_code(name):
code.globalstate.use_utility_code(acquire_utility_code)
itemchecker = get_ts_check_item(dtype, code)
+ dtype_cname = dtype.declaration_code("")
utilcode = [dedent("""
- static int %s(PyObject* obj, Py_buffer* buf, int flags, int nd); /*proto*/
+ static int %s(PyObject* obj, Py_buffer* buf, int flags, int nd, int cast); /*proto*/
""") % name, dedent("""
- static int %(name)s(PyObject* obj, Py_buffer* buf, int flags, int nd) {
+ static int %(name)s(PyObject* obj, Py_buffer* buf, int flags, int nd, int cast) {
const char* ts;
if (obj == Py_None) {
__Pyx_ZeroBuffer(buf);
__Pyx_BufferNdimError(buf, nd);
goto fail;
}
- ts = buf->format;
- ts = __Pyx_ConsumeWhitespace(ts);
- if (!ts) goto fail;
- ts = %(itemchecker)s(ts);
- if (!ts) goto fail;
- ts = __Pyx_ConsumeWhitespace(ts);
- if (!ts) goto fail;
- if (*ts != 0) {
- PyErr_Format(PyExc_ValueError,
- "Expected non-struct buffer data type (expected end, got '%%s')", ts);
- goto fail;
+ if (!cast) {
+ ts = buf->format;
+ ts = __Pyx_ConsumeWhitespace(ts);
+ if (!ts) goto fail;
+ ts = %(itemchecker)s(ts);
+ if (!ts) goto fail;
+ ts = __Pyx_ConsumeWhitespace(ts);
+ if (!ts) goto fail;
+ if (*ts != 0) {
+ PyErr_Format(PyExc_ValueError,
+ "Expected non-struct buffer data type (expected end, got '%%s')", ts);
+ goto fail;
+ }
+ } else {
+ if (buf->itemsize != sizeof(%(dtype_cname)s)) {
+ PyErr_SetString(PyExc_ValueError,
+ "Attempted cast of buffer to datatype of different size.");
+ goto fail;
+ }
}
if (buf->suboffsets == NULL) buf->suboffsets = __Pyx_minusones;
return 0;
"""
buf[idx] = obj
+#
+# cast option
+#
+@testcase
+def buffer_cast(object[unsigned int, cast=True] buf, int idx):
+ """
+ Round-trip a signed int through unsigned int buffer access.
+ >>> A = IntMockBuffer(None, [-100])
+ >>> buffer_cast(A, 0)
+ -100
+ """
+ cdef unsigned int data = buf[idx]
+ return <int>data
+
+@testcase
+def buffer_cast_fails(object[char, cast=True] buf):
+ """
+ Cannot cast between datatype of different sizes.
+
+ >>> buffer_cast_fails(IntMockBuffer(None, [0]))
+ Traceback (most recent call last):
+ ...
+ ValueError: Attempted cast of buffer to datatype of different size.
+ """
+ return buf[0]
#
cdef get_default_format(self):
print "ERROR, not subclassed", self.__class__
+cdef class CharMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ (<char*>buf)[0] = <int>value
+ return 0
+ cdef get_itemsize(self): return sizeof(char)
+ cdef get_default_format(self): return b"@b"
+
cdef class IntMockBuffer(MockBuffer):
cdef int write(self, char* buf, object value) except -1:
(<int*>buf)[0] = <int>value