Skip to main content

Modules/arraymodule.c

Source:

cpython 3.14 @ ab2d84fe1023/Modules/arraymodule.c

array.array provides a typed, compact array of C primitives. It is more memory-efficient than a list of ints/floats and supports the buffer protocol for zero-copy I/O.

Map

LinesSymbolRole
1-100Type descriptor tableMaps type code ('i', 'd', etc.) to C type info
101-300array_newConstruction from type code + optional initializer
301-500array_getitem, array_setitemIndex/slice access
501-700array_append, array_extendAppending elements
701-900array_frombytes, array_tobytesBinary import/export
901-1100array_fromfile, array_tofileRead/write from a file object
1101-1300array_byteswapReverse byte order in place
1301-1500Arithmetic+ (concat), * (repeat)
1501-2000Buffer protocolbf_getbuffer — expose raw C array
2001-2500Type descriptorsarraydescr for each of 13 type codes

Reading

Type descriptor

// CPython: Modules/arraymodule.c:72 arraydescr
struct arraydescr {
char typecode; /* 'i', 'd', etc. */
int itemsize; /* bytes per element */
PyObject *(*getitem)(arrayobject *, Py_ssize_t);
int (*setitem)(arrayobject *, Py_ssize_t, PyObject *);
int (*compareitems)(const void *, const void *, Py_ssize_t);
const char *formats; /* struct format string */
int is_integer_type;
int is_signed;
};

The 13 supported type codes: b/B (signed/unsigned char), h/H (short), i/I (int), l/L (long), q/Q (long long), f (float), d (double). u (Unicode) was removed in 3.13.

array_new

// CPython: Modules/arraymodule.c:165 array_new
static PyObject *
array_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
char typecode;
PyArg_ParseTuple(args, "C|O:array", &typecode, &initial);
/* Find descriptor for typecode */
const struct arraydescr *descr = NULL;
for (int i = 0; i < NUM_TYPECODES; i++) {
if (descriptors[i].typecode == typecode) { descr = &descriptors[i]; break; }
}
arrayobject *a = (arrayobject *)type->tp_alloc(type, 0);
a->ob_descr = descr;
a->ob_item = NULL;
a->allocated = 0;
/* Initialize from iterable, bytes, or existing array */
...
}

array_append

// CPython: Modules/arraymodule.c:528 array_append
static PyObject *
array_append(arrayobject *self, PyObject *v)
{
/* Resize if needed (amortized growth) */
Py_ssize_t n = Py_SIZE(self);
if (array_resize(self, n + 1) == -1) return NULL;
/* Store via type-specific setitem */
return (self->ob_descr->setitem)(self, n, v) == -1 ? NULL : Py_NewRef(Py_None);
}

frombytes / tobytes

// CPython: Modules/arraymodule.c:748 array_frombytes
static PyObject *
array_frombytes(arrayobject *self, PyObject *buffer_obj)
{
Py_buffer buffer;
PyObject_GetBuffer(buffer_obj, &buffer, PyBUF_SIMPLE);
Py_ssize_t n = buffer.len / self->ob_descr->itemsize;
if (buffer.len % self->ob_descr->itemsize != 0) {
PyErr_SetString(PyExc_ValueError, "bytes length not a multiple of item size");
return NULL;
}
Py_ssize_t old_n = Py_SIZE(self);
array_resize(self, old_n + n);
memcpy(self->ob_item + old_n * self->ob_descr->itemsize,
buffer.buf, buffer.len);
PyBuffer_Release(&buffer);
Py_RETURN_NONE;
}

frombytes and tobytes do direct memory copies — no element-by-element conversion.

Buffer protocol

// CPython: Modules/arraymodule.c:1640 array_buffer_getbuf
static int
array_buffer_getbuf(arrayobject *self, Py_buffer *view, int flags)
{
return PyBuffer_FillInfo(view, (PyObject *)self,
self->ob_item,
Py_SIZE(self) * self->ob_descr->itemsize,
0 /* writable */,
flags);
}

Passing an array.array('d', [...]) to numpy.frombuffer() is zero-copy.

gopy notes

array module is in module/array/. The backing store is a Go []byte with the itemsize from the descriptor. Type conversion uses Go's binary.Read/binary.Write with native byte order. The buffer protocol exposes the []byte slice directly.