Skip to main content

Lib/struct.py / Modules/_struct.c (part 2)

Source:

cpython 3.14 @ ab2d84fe1023/Modules/_struct.c

This annotation covers the Struct class methods and native alignment. See modules_struct_detail for struct.pack/struct.unpack, format codes, and byte order prefixes.

Map

LinesSymbolRole
1-80Struct.__new__Compile a format string; cache size and alignment
81-200Struct.pack_intoPack directly into a writable buffer at an offset
201-350Struct.unpack_fromUnpack from buffer at an offset
351-500Struct.iter_unpackReturn an iterator that unpacks successive chunks
501-650Native alignment (@)Align each field to its natural C alignment
651-800calcsizeReturn the byte size of a format string
801-1200Format caching_struct.cache keeps recently-used Struct objects

Reading

Struct.pack_into

// CPython: Modules/_struct.c:1480 s_pack_into
static PyObject *
s_pack_into(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
{
/* pack_into(buffer, offset, v1, v2, ...) */
/* Get a writable buffer view */
Py_buffer buffer;
if (PyObject_GetBuffer(args[0], &buffer, PyBUF_WRITABLE) < 0) return NULL;
Py_ssize_t offset = PyLong_AsSsize_t(args[1]);
if (offset < 0) offset += buffer.len; /* negative indexing */
if (offset < 0 || offset + soself->s_size > buffer.len) {
PyErr_Format(StructError, "pack_into requires a buffer of at least %zd bytes",
offset + soself->s_size);
PyBuffer_Release(&buffer);
return NULL;
}
/* Pack into buffer.buf + offset */
if (s_object_pack(soself, args + 2, nargs - 2,
(char *)buffer.buf + offset) < 0) { ... }
PyBuffer_Release(&buffer);
Py_RETURN_NONE;
}

pack_into is used to write struct data directly into a bytearray or mmap without allocating a new bytes object.

Struct.iter_unpack

// CPython: Modules/_struct.c:1620 s_iter_unpack
/* Returns an iterator that calls unpack() repeatedly on successive
non-overlapping chunks of the buffer. */
static PyObject *
s_iter_unpack(PyObject *self, PyObject *buffer_obj)
{
unpackiter *iter = PyObject_GC_New(unpackiter, &unpackiter_type);
iter->buf = buffer;
iter->s = soself;
iter->index = 0;
return (PyObject *)iter;
}

struct.iter_unpack('!I', data) is equivalent to [struct.unpack('!I', data[i:i+4]) for i in range(0, len(data), 4)] but lazy.

Native alignment (@)

// CPython: Modules/_struct.c:600 native alignment
/* '@' prefix: native byte order + native alignment (C struct layout)
Fields are padded so each starts at a multiple of its size:
'@HI': H is at offset 0, pad byte at 2, I starts at offset 4.
Compare with '=' (native order, no alignment padding).
*/

struct.calcsize('@HI') may return 8 (2 + 2 pad + 4) while struct.calcsize('=HI') returns 6 (2 + 4).

Format caching

// CPython: Modules/_struct.c:820 cache
/* struct.pack/unpack calls intern the Struct object for the format string.
A per-interpreter LRU cache of ~8 entries avoids recompilation for
commonly-used format strings. */

gopy notes

struct is accelerated by _struct C module. gopy's Go implementation is in module/struct/module.go. pack_into uses objects.GetWritableBuffer to get a []byte slice. iter_unpack returns a Go generator backed by struct.UnpackIterator. The native alignment @ uses unsafe.Alignof via a generated alignment table.