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.