Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
daf8e02
gh-139871: Update bytearray to contain PyBytesObject
cmaloney Oct 3, 2025
39b2d15
📜🤖 Added by blurb_it.
blurb-it[bot] Oct 14, 2025
86faf1d
Add bytearray.take_bytes
cmaloney Oct 3, 2025
a9328f4
Merge branch 'main' into bytearray_bytes
cmaloney Oct 15, 2025
e9f5ca9
Update Objects/bytearrayobject.c
cmaloney Oct 15, 2025
4784957
Review fixes
cmaloney Oct 15, 2025
db19def
Merge branch 'main' into bytearray_bytes
cmaloney Oct 15, 2025
451c302
Update Objects/bytearrayobject.c
cmaloney Oct 17, 2025
bab7151
Add tests around alloc and getsizeof that show clearing isn't working…
cmaloney Oct 15, 2025
cb2377c
Fix resizing to 0 length / clearing leaving one byte alloc
cmaloney Oct 15, 2025
20175f8
review fix: handle NULL return from from PyBytes_FromStringAndSize
cmaloney Oct 18, 2025
e485595
Add take_bytes to test_free_threading
cmaloney Oct 18, 2025
b5535d0
Missed line...
cmaloney Oct 18, 2025
7c6e8a8
Simplify getting out ob_bytes
cmaloney Oct 18, 2025
4e27d13
Include PyBytesObject in __alloc__ of bytearray.
cmaloney Oct 18, 2025
9887dad
Apply suggestion from @vstinner
cmaloney Oct 27, 2025
6e4b910
Don't multiply by sizeof(char) as it's always 1
cmaloney Oct 27, 2025
b6f8403
Rely on bytes for end of buffer NULL
cmaloney Oct 27, 2025
28cb8c5
Personal review fixes
cmaloney Oct 27, 2025
f03b895
Simplify resize error handling
cmaloney Oct 27, 2025
a45f3c2
Use right PyLong constructor
cmaloney Oct 27, 2025
c8943e3
Add a define for max bytearray size, comment size=0
cmaloney Oct 29, 2025
5bffb7e
Remove oold comment
cmaloney Oct 29, 2025
583ea4b
Update test_capi.test_bytearray for MemoryError vs OverflowError
cmaloney Oct 29, 2025
8ee14e6
More accurate size and alloc calculation
cmaloney Oct 29, 2025
d70e369
Comment and minor doc tweaks
cmaloney Oct 29, 2025
97be818
Apply suggestion from @vstinner
cmaloney Oct 29, 2025
99e49ef
Remove _PyByteArray_empty_string, add bytearray_reinit_from_bytes
cmaloney Oct 29, 2025
f4b62d9
Update Stable API concerns: restore _empty_string an dmove _PyBytesOb…
cmaloney Oct 29, 2025
48afb62
Restore _PyByteArray_empty_string in .c file
cmaloney Oct 29, 2025
8c81e03
remove line that shouldn't have been added
cmaloney Oct 29, 2025
313e78c
Apply suggestion from @encukou
cmaloney Oct 30, 2025
c028e2b
Remove original variable, no longer used
cmaloney Oct 30, 2025
a69b338
remove _PyByteArray_empty_string
cmaloney Oct 30, 2025
2a95118
Add take_bytes_n free-threading test
cmaloney Oct 30, 2025
9680e8a
Expand comment for ob_alloc
cmaloney Oct 31, 2025
02882af
Add note on memmove tradeoff
cmaloney Oct 31, 2025
b67d10c
Move suggested optimizing refactors to whatsnew
cmaloney Oct 31, 2025
6db8822
Remove unintended change
cmaloney Oct 31, 2025
fb84c14
Fix intermittent failure on deallocation from uninitialized memory
cmaloney Nov 4, 2025
0258891
PEP 7
cmaloney Nov 4, 2025
c470178
Tweak comment
cmaloney Nov 4, 2025
9681135
Add +1 so allocation is over max byte length
cmaloney Nov 7, 2025
442692a
Minor tweak for flow in whatsnew entry
cmaloney Nov 7, 2025
ee0d6d6
Apply suggestions from code review
cmaloney Nov 7, 2025
cc238c2
Merge branch 'main' into bytearray_bytes
encukou Nov 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Remove _PyByteArray_empty_string, add bytearray_reinit_from_bytes
De-duplicate the code to set `ob_bytes`, `ob_start`, `ob_alloc` and `ob_size`; rely in resize on ob_start being always set.
  • Loading branch information
cmaloney authored Oct 29, 2025
commit 99e49efa7dc3a57168766ea51c6adca754868576
8 changes: 1 addition & 7 deletions Include/cpython/bytearrayobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,13 @@ typedef struct {
PyObject *ob_bytes_object; /* PyBytes for zero-copy bytes conversion */
Comment thread
cmaloney marked this conversation as resolved.
} PyByteArrayObject;

PyAPI_DATA(char) _PyByteArray_empty_string[];
Comment thread
cmaloney marked this conversation as resolved.

/* Macros and static inline functions, trading safety for speed */
#define _PyByteArray_CAST(op) \
(assert(PyByteArray_Check(op)), _Py_CAST(PyByteArrayObject*, op))

static inline char* PyByteArray_AS_STRING(PyObject *op)
{
PyByteArrayObject *self = _PyByteArray_CAST(op);
if (Py_SIZE(self)) {
return self->ob_start;
}
return _PyByteArray_empty_string;
return _PyByteArray_CAST(op)->ob_start;
}
#define PyByteArray_AS_STRING(self) PyByteArray_AS_STRING(_PyObject_CAST(self))

Expand Down
48 changes: 20 additions & 28 deletions Objects/bytearrayobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ class bytearray "PyByteArrayObject *" "&PyByteArray_Type"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=5535b77c37a119e0]*/

/* For PyByteArray_AS_STRING(). */
char _PyByteArray_empty_string[] = "";
Comment thread
cmaloney marked this conversation as resolved.

/* Max number of bytes a bytearray can contain */
#define PyByteArray_SIZE_MAX ((Py_ssize_t)(PY_SSIZE_T_MAX - _PyBytesObject_SIZE))

Expand All @@ -46,6 +43,14 @@ _getbytevalue(PyObject* arg, int *value)
return 1;
}

static void
bytearray_reinit_from_bytes(PyByteArrayObject *self, Py_ssize_t size,
Py_ssize_t alloc) {
self->ob_bytes = self->ob_start = PyBytes_AS_STRING(self->ob_bytes_object);
Py_SET_SIZE(self, size);
FT_ATOMIC_STORE_SSIZE_RELAXED(self->ob_alloc, alloc);
}

static int
bytearray_getbuffer_lock_held(PyObject *self, Py_buffer *view, int flags)
{
Expand Down Expand Up @@ -151,14 +156,10 @@ PyByteArray_FromStringAndSize(const char *bytes, Py_ssize_t size)
Py_DECREF(new);
return NULL;
}
new->ob_bytes = PyBytes_AS_STRING(new->ob_bytes_object);
assert(new->ob_bytes);
bytearray_reinit_from_bytes(new, size, size);
if (bytes != NULL && size > 0) {
memcpy(new->ob_bytes, bytes, size);
}
Py_SET_SIZE(new, size);
new->ob_alloc = size;
new->ob_start = new->ob_bytes;
new->ob_exports = 0;

return (PyObject *)new;
Expand Down Expand Up @@ -247,26 +248,12 @@ bytearray_resize_lock_held(PyObject *self, Py_ssize_t requested_size)
Py_MIN(requested_size, Py_SIZE(self)));
Comment thread
encukou marked this conversation as resolved.
}

if (obj->ob_bytes_object == NULL) {
obj->ob_bytes_object = PyBytes_FromStringAndSize(NULL, alloc);
}
else {
_PyBytes_Resize(&obj->ob_bytes_object, alloc);
}

int ret;
if (obj->ob_bytes_object == NULL) {
int ret = _PyBytes_Resize(&obj->ob_bytes_object, alloc);
if (ret == -1) {
obj->ob_bytes_object = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
size = alloc = 0;
ret = -1;
}
else {
ret = 0;
}

obj->ob_bytes = obj->ob_start = PyBytes_AS_STRING(obj->ob_bytes_object);
Py_SET_SIZE(self, size);
FT_ATOMIC_STORE_SSIZE_RELAXED(obj->ob_alloc, alloc);
bytearray_reinit_from_bytes(obj, size, alloc);
if (alloc != size) {
/* Add mid-buffer null; end provided by bytes. */
obj->ob_bytes[size] = '\0';
Expand Down Expand Up @@ -904,6 +891,13 @@ bytearray___init___impl(PyByteArrayObject *self, PyObject *arg,
PyObject *it;
PyObject *(*iternext)(PyObject *);

/* First __init__; set ob_bytes_object so ob_bytes is always non-null. */
Comment thread
cmaloney marked this conversation as resolved.
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;
}

if (Py_SIZE(self) != 0) {
/* Empty previous contents (yes, do this first of all!) */
if (PyByteArray_Resize((PyObject *)self, 0) < 0)
Expand Down Expand Up @@ -1564,9 +1558,7 @@ bytearray_take_bytes_impl(PyByteArrayObject *self, PyObject *n)
// Point the bytearray towards the buffer with the remaining data.
PyObject *result = self->ob_bytes_object;
self->ob_bytes_object = remaining;
self->ob_bytes = self->ob_start = PyBytes_AS_STRING(self->ob_bytes_object);
Py_SET_SIZE(self, remaining_length);
FT_ATOMIC_STORE_SSIZE_RELAXED(self->ob_alloc, remaining_length);
bytearray_reinit_from_bytes(self, remaining_length, remaining_length);
return result;
}

Expand Down
1 change: 0 additions & 1 deletion Tools/c-analyzer/cpython/ignored.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,6 @@ Modules/pyexpat.c - error_info_of -
Modules/pyexpat.c - handler_info -
Modules/termios.c - termios_constants -
Modules/timemodule.c init_timezone YEAR -
Objects/bytearrayobject.c - _PyByteArray_empty_string -
Objects/complexobject.c - c_1 -
Objects/exceptions.c - static_exceptions -
Objects/genobject.c - ASYNC_GEN_IGNORED_EXIT_MSG -
Expand Down
Loading