Skip to main content

Objects/bytearrayobject.c

cpython 3.14 @ ab2d84fe1023/Objects/bytearrayobject.c

bytearrayobject.c defines the bytearray built-in type. Unlike bytes, a bytearray is mutable: it owns a heap-allocated char * buffer (ob_val), tracks the allocated capacity in ob_alloc, and counts active buffer-protocol exports in ob_exports. Every mutation must check ob_exports first; a non-zero count means external code is reading the buffer and the mutation must be rejected. The file also supplies the full bytes-like method suite (find, replace, split, translate, decode, etc.) by sharing helper functions with bytesobject.c.

Map​

LinesSymbolRolegopy
1-60includes, _getbytevalue helperHeader; coerce an int-or-bytes argument to a single byte valuenot ported
61-200bytearray_buffer_getbuffer, bytearray_buffer_releasebufferBuffer-protocol slots; guard mutations via ob_exportsnot ported
201-360bytearray_resizeGrow or shrink ob_val; applies 1.125x overallocation on growthnot ported
361-500PyByteArray_FromStringAndSize, PyByteArray_FromObjectPublic constructorsnot ported
501-620PyByteArray_Size, PyByteArray_AsString, PyByteArray_ResizePublic C APInot ported
621-760bytearray_concat, bytearray_repeat, bytearray_irepeatSequence slots for + and *not ported
761-880bytearray_getitem, bytearray_setitem, bytearray_delitemItem and slice access/mutationnot ported
881-1020bytearray_subscript, bytearray_ass_subscriptmp_subscript and mp_ass_subscript for extended slicingnot ported
1021-1160bytearray_contains, bytearray_richcomparein operator and comparisonnot ported
1161-1300bytearray_append, bytearray_extend, bytearray_insertMutation methodsnot ported
1301-1420bytearray_pop, bytearray_remove, bytearray_reverseMore mutation methodsnot ported
1421-1580bytearray_find, bytearray_rfind, bytearray_index, bytearray_rindexSearch methods delegating to Bytes_Findnot ported
1581-1720bytearray_count, bytearray_startswith, bytearray_endswithCounting and prefix/suffix testsnot ported
1721-1880bytearray_replace, bytearray_translateReplace and translate with optional delete setnot ported
1881-2020bytearray_split, bytearray_rsplit, bytearray_splitlinesSplit familynot ported
2021-2160bytearray_join, bytearray_strip, bytearray_lstrip, bytearray_rstripJoin and stripnot ported
2161-2300bytearray_decode, bytearray_encodeCodec integration via PyUnicode_AsEncodedStringnot ported
2301-2400bytearray_repr, bytearray_iterRepr and iteratornot ported
2401-2500PyByteArray_Type definitionFull PyTypeObjectnot ported

Reading​

Buffer protocol and export guard (lines 61 to 200)​

cpython 3.14 @ ab2d84fe1023/Objects/bytearrayobject.c#L61-200

Because bytearray is mutable, any code that holds a raw pointer into ob_val through the buffer protocol must prevent concurrent mutation. CPython tracks this with ob_exports, an integer on the object itself. bytearray_buffer_getbuffer increments it; bytearray_buffer_releasebuffer decrements it. Every mutating method starts with a guard:

static int
bytearray_buffer_getbuffer(PyByteArrayObject *self,
Py_buffer *view, int flags)
{
CHECK_SIZE_T();
if (PyBuffer_FillInfo(view, (PyObject *)self,
PyByteArray_AS_STRING(self),
Py_SIZE(self), 0, flags) < 0)
return -1;
self->ob_exports++;
return 0;
}

A method like bytearray_resize calls this check before touching ob_val:

if (self->ob_exports > 0 && size != Py_SIZE(self)) {
PyErr_SetString(PyExc_BufferError,
"Existing exports of data: object cannot be re-sized");
return -1;
}

This is the same pattern that prevents a bytearray from being modified while a memoryview holds a live reference to it.

Growth policy in bytearray_resize (lines 201 to 360)​

cpython 3.14 @ ab2d84fe1023/Objects/bytearrayobject.c#L201-360

bytearray_resize is the single allocation function used by every method that adds bytes. When the requested size exceeds the current allocation, it overallocates by roughly 12.5% (1.125x) to amortise the cost of repeated append calls:

static int
bytearray_resize(PyByteArrayObject *self, Py_ssize_t size)
{
Py_ssize_t alloc = self->ob_alloc;

if (size < alloc / 2) {
/* shrink aggressively only when size drops below half */
alloc = size + 1;
} else if (size < alloc) {
return 0; /* enough room already */
} else {
/* grow: add ~12.5% headroom */
alloc = size + (size >> 3) + (size < 9 ? 3 : 6);
}

sval = PyObject_Realloc(self->ob_val, alloc);
...
self->ob_val = sval;
self->ob_alloc = alloc;
Py_SET_SIZE(self, size);
self->ob_val[size] = '\0'; /* null-terminate for C interop */
return 0;
}

The null terminator at ob_val[size] is written even though bytearray is not a C string. It is there so that PyByteArray_AsString can hand the pointer to C code that expects a null-terminated buffer.

bytearray_translate and the delete set (lines 1721 to 1880)​

cpython 3.14 @ ab2d84fe1023/Objects/bytearrayobject.c#L1721-1880

bytearray.translate(table, delete=b'') applies a 256-byte lookup table and simultaneously removes bytes listed in the delete set. The implementation makes a single pass, writing survivors into a result buffer:

static PyObject *
bytearray_translate(PyByteArrayObject *self, PyObject *args)
{
...
/* build a boolean delete-set from the delete argument */
if (del_obj != NULL) {
for (i = 0; i < dlen; i++)
del_table[(unsigned char)del[i]] = 1;
}
/* translate loop */
for (i = 0; i < inlen; i++) {
c = (unsigned char) input[i];
if (del_table[c])
continue; /* deleted */
if (table != NULL)
output[outlen++] = table[c];
else
output[outlen++] = c;
}
...
}

Using a 256-entry boolean array for deletion keeps the loop O(n) with no hash lookups. This mirrors the same design used in bytesobject.c and unicodeobject.c.

gopy mirror​

bytearrayobject.c has no counterpart in gopy yet. The natural home for a future port is objects/bytearray.go. Go slices already carry length and capacity separately, so the 1.125x overallocation policy can be expressed with append semantics or a manual grow helper. The export-guard pattern maps to an atomic integer that mutation methods check before proceeding. The method suite can share helpers with a bytes object port, mirroring the CPython approach of reusing Bytes_Find and friends.

CPython 3.14 changes​

CPython 3.14 added bytearray.resize(size) as a public method (previously only PyByteArray_Resize was available from C). The bytearray_decode path gained support for the errors='surrogatepass' handler consistently with bytes.decode. Internally, the ob_exports guard was also applied to the in-place repeat operator (*=), closing a bug where concurrent buffer users could observe a partial re-allocation.