self.assertRaises(BufferError, ba.hex, S(b':'))
+ def test_no_init_called(self):
+ # A bytearray created without calling bytearray.__init__
+ # should not crash the interpreter (see gh-153419).
+ def bytearray_new():
+ return bytearray.__new__(bytearray)
+
+ bytearray_new().insert(0, 1)
+ bytearray_new().extend(b"x")
+ bytearray_new().extend([1, 2, 3])
+ bytearray_new().resize(4)
+ bytearray_new().__init__(5)
+ bytearray_new().__init__(b"xyz")
+ bytearray_new().take_bytes()
+ bytearray_new().take_bytes(0)
+
+ a = bytearray_new()
+ a.append(1)
+
+ a = bytearray_new()
+ a += b"x"
+
+ a = bytearray_new()
+ a[:] = b"xyz"
+
+ def test_reinit_length(self):
+ # There is a shortcut taken when resizing, where alloc/2 < newsize.
+ # In this case, the existing buffer is reused, rather than reset.
+ # If this happens when newsize == 0 and alloc == 1, then various
+ # code assumptions can be violated. This test should catch those
+ # in debug builds. (see gh-153419)
+ a = bytearray(1)
+ a.__init__()
+ self.assertEqual(a, b"")
+
+ def test_reinit_with_view(self):
+ a = bytearray()
+ with memoryview(a):
+ self.assertRaises(BufferError, a.__init__, "x", "ascii")
+ self.assertEqual(a, b"")
+
class AssortedBytesTest(unittest.TestCase):
#
{
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
PyByteArrayObject *obj = ((PyByteArrayObject *)self);
+
+ assert(obj->ob_bytes_object != NULL);
+
/* All computations are done unsigned to avoid integer overflows
(see issue #22335). */
size_t alloc = (size_t) obj->ob_alloc;
return -1;
}
+ /* Resize to 0 resets to empty bytes (see issue #153419). */
+ if (requested_size == 0) {
+ Py_SETREF(obj->ob_bytes_object,
+ Py_GetConstant(Py_CONSTANT_EMPTY_BYTES));
+ bytearray_reinit_from_bytes(obj, 0, 0);
+ return 0;
+ }
+
if (size + logical_offset <= alloc) {
/* Current buffer is large enough to host the requested size,
decide on a strategy. */
return ret;
}
+static PyObject *
+bytearray_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyObject *op = PyType_GenericNew(type, args, kwds);
+ if (op == NULL) {
+ return NULL;
+ }
+ PyByteArrayObject *self = _PyByteArray_CAST(op);
+ self->ob_bytes_object = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
+ bytearray_reinit_from_bytes(self, 0, 0);
+ self->ob_exports = 0;
+ return op;
+}
+
/*[clinic input]
bytearray.__init__
PyObject *it;
PyObject *(*iternext)(PyObject *);
- /* First __init__; set ob_bytes_object so ob_bytes is always non-null. */
- if (self->ob_bytes_object == NULL) {
- self->ob_bytes_object = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
- bytearray_reinit_from_bytes(self, 0, 0);
- self->ob_exports = 0;
+ /* Disallow any __init__ call if the object is not resizable (has exports)
+ to make the handling of non-null `source` init values simpler. */
+ if (!_canresize(self)) {
+ return -1;
}
- if (Py_SIZE(self) != 0) {
- /* Empty previous contents (yes, do this first of all!) */
- if (PyByteArray_Resize((PyObject *)self, 0) < 0)
- return -1;
+ /* Empty any previous contents (do this first of all!). */
+ if (PyByteArray_Resize((PyObject *)self, 0) < 0) {
+ return -1;
}
-
- /* Should be caused by first init or the resize to 0. */
assert(self->ob_bytes_object == Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_BYTES));
assert(self->ob_exports == 0);
}
if (_PyBytes_Resize(&self->ob_bytes_object, to_take) == -1) {
+ assert(self->ob_bytes_object == NULL);
+ self->ob_bytes_object = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
+ bytearray_reinit_from_bytes(self, 0, 0);
Py_DECREF(remaining);
return NULL;
}
0, /* tp_dictoffset */
bytearray___init__, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
- PyType_GenericNew, /* tp_new */
+ bytearray_new, /* tp_new */
PyObject_Free, /* tp_free */
.tp_version_tag = _Py_TYPE_VERSION_BYTEARRAY,
};