m = memoryview(data).cast(complex_format)
check_equal(m, True)
+ def test_boolean_format(self):
+ # Test '?' format (keep all the checks below for UBSan)
+ # See github.com/python/cpython/issues/148390.
+
+ # m1a and m1b are equivalent to [False, True, False]
+ m1a = memoryview(b'\0\2\0').cast('?')
+ self.assertEqual(m1a.tolist(), [False, True, False])
+ m1b = memoryview(b'\0\4\0').cast('?')
+ self.assertEqual(m1b.tolist(), [False, True, False])
+ self.assertEqual(m1a, m1b)
+
+ # m2a and m2b are equivalent to [True, True, True]
+ m2a = memoryview(b'\1\3\5').cast('?')
+ self.assertEqual(m2a.tolist(), [True, True, True])
+ m2b = memoryview(b'\2\4\6').cast('?')
+ self.assertEqual(m2b.tolist(), [True, True, True])
+ self.assertEqual(m2a, m2b)
+
+ allbytes = bytes(range(256))
+ allbytes = memoryview(allbytes).cast('?')
+ self.assertEqual(allbytes.tolist(), [False] + [True] * 255)
+
class BytesMemorySliceTest(unittest.TestCase,
BaseMemorySliceTests, BaseBytesMemoryTests):
--- /dev/null
+Fix an undefined behavior in :class:`memoryview` when using the native
+boolean format (``?``) in :meth:`~memoryview.cast`. Previously, on some
+common platforms, calling ``memoryview(b).cast("?").tolist()`` incorrectly
+returned ``[False]`` instead of ``[True]`` for any even byte *b*.
+Patch by Bénédikt Tran.
return -1;
}
+// UNPACK_TO_BOOL: Return 0 if PTR represents "false", and 1 otherwise.
+static const _Bool bool_false = 0;
+#define UNPACK_TO_BOOL(PTR) (memcmp((PTR), &bool_false, sizeof(_Bool)) != 0)
+
/* Accept integer objects or objects with an __index__() method. */
static long
pylong_as_ld(PyObject *item)
case 'l': UNPACK_SINGLE(ld, ptr, long); goto convert_ld;
/* boolean */
- case '?': UNPACK_SINGLE(ld, ptr, _Bool); goto convert_bool;
+ case '?': ld = UNPACK_TO_BOOL(ptr); goto convert_bool;
/* unsigned integers */
case 'H': UNPACK_SINGLE(lu, ptr, unsigned short); goto convert_lu;
case 'l': CMP_SINGLE(p, q, long); return equal;
/* boolean */
- case '?': CMP_SINGLE(p, q, _Bool); return equal;
+ case '?': return UNPACK_TO_BOOL(p) == UNPACK_TO_BOOL(q);
/* unsigned integers */
case 'H': CMP_SINGLE(p, q, unsigned short); return equal;
##################################
## global non-objects to fix in builtin modules
-# <none>
+Objects/memoryobject.c - bool_false -
##################################
# Objects/object.c:97:5: runtime error: member access within null pointer of type 'PyThreadState' (aka 'struct _ts')
null:Objects/object.c
-# Objects/memoryobject.c:3032:15: runtime error: load of value 2, which is not a valid value for type 'bool'
-bool:Objects/memoryobject.c
-
# Modules/_ctypes/cfield.c:644:1: runtime error: left shift of 1 by 63 places cannot be represented in type 'int64_t' (aka 'long')
shift-base:Modules/_ctypes/cfield.c