Issue #29300: Convert _struct module to Argument Clinic

* The struct module now requires contiguous buffers.
* Convert most functions and methods of the _struct module to Argument Clinic
* Use "Py_buffer" type for the "buffer" argument. Argument Clinic is
  responsible to create and release the Py_buffer object.
* Use "PyStructObject *" type for self to avoid explicit conversions.
* Add an unit test on the _struct.Struct.unpack_from() method to test passing
  arguments as keywords.
* Rephrase docstrings.
* Rename "fmt" argument to "format" in docstrings and the documentation.

As a side effect, functions and methods which used METH_VARARGS calling
convention like struct.pack() now use the METH_FASTCALL calling convention
which avoids the creation of temporary tuple to pass positional arguments and
so is faster. For example, struct.pack("i", 1) becomes 1.56x faster (-36%)::

    $ ./python -m perf timeit \
        -s 'import struct; pack=struct.pack' 'pack("i", 1)' \
        --compare-to=../default-ref/python
    Median +- std dev: 119 ns +- 1 ns -> 76.8 ns +- 0.4 ns: 1.56x faster (-36%)
    Significant (t=295.91)

Patch co-written with Serhiy Storchaka.
diff --git a/Modules/_struct.c b/Modules/_struct.c
index d621789..3626bad 100644
--- a/Modules/_struct.c
+++ b/Modules/_struct.c
@@ -9,6 +9,11 @@
 #include "structmember.h"
 #include <ctype.h>
 
+/*[clinic input]
+class Struct "PyStructObject *" "&PyStructType"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=9b032058a83ed7c3]*/
+
 static PyTypeObject PyStructType;
 
 /* The translation function for each format character is table driven */
@@ -80,6 +85,8 @@
 #pragma options align=reset
 #endif
 
+#include "clinic/_struct.c.h"
+
 /* Helper for integer format codes: converts an arbitrary Python object to a
    PyLongObject if possible, otherwise fails.  Caller should decref. */
 
@@ -1429,41 +1436,46 @@
     return self;
 }
 
+/*[clinic input]
+Struct.__init__
+
+    format: object
+
+Create a compiled struct object.
+
+Return a new Struct object which writes and reads binary data according to
+the format string.
+
+See help(struct) for more on format strings.
+[clinic start generated code]*/
+
 static int
-s_init(PyObject *self, PyObject *args, PyObject *kwds)
+Struct___init___impl(PyStructObject *self, PyObject *format)
+/*[clinic end generated code: output=b8e80862444e92d0 input=192a4575a3dde802]*/
 {
-    PyStructObject *soself = (PyStructObject *)self;
-    PyObject *o_format = NULL;
     int ret = 0;
-    static char *kwlist[] = {"format", 0};
 
-    assert(PyStruct_Check(self));
-
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:Struct", kwlist,
-                                     &o_format))
-        return -1;
-
-    if (PyUnicode_Check(o_format)) {
-        o_format = PyUnicode_AsASCIIString(o_format);
-        if (o_format == NULL)
+    if (PyUnicode_Check(format)) {
+        format = PyUnicode_AsASCIIString(format);
+        if (format == NULL)
             return -1;
     }
     /* XXX support buffer interface, too */
     else {
-        Py_INCREF(o_format);
+        Py_INCREF(format);
     }
 
-    if (!PyBytes_Check(o_format)) {
-        Py_DECREF(o_format);
+    if (!PyBytes_Check(format)) {
+        Py_DECREF(format);
         PyErr_Format(PyExc_TypeError,
                      "Struct() argument 1 must be a bytes object, not %.200s",
-                     Py_TYPE(o_format)->tp_name);
+                     Py_TYPE(format)->tp_name);
         return -1;
     }
 
-    Py_XSETREF(soself->s_format, o_format);
+    Py_XSETREF(self->s_format, format);
 
-    ret = prepare_s(soself);
+    ret = prepare_s(self);
     return ret;
 }
 
@@ -1517,78 +1529,69 @@
 }
 
 
-PyDoc_STRVAR(s_unpack__doc__,
-"S.unpack(buffer) -> (v1, v2, ...)\n\
-\n\
-Return a tuple containing values unpacked according to the format\n\
-string S.format.  The buffer's size in bytes must be S.size.  See\n\
-help(struct) for more on format strings.");
+/*[clinic input]
+Struct.unpack
+
+    buffer: Py_buffer
+    /
+
+Return a tuple containing unpacked values.
+
+Unpack according to the format string Struct.format. The buffer's size
+in bytes must be Struct.size.
+
+See help(struct) for more on format strings.
+[clinic start generated code]*/
 
 static PyObject *
-s_unpack(PyObject *self, PyObject *input)
+Struct_unpack_impl(PyStructObject *self, Py_buffer *buffer)
+/*[clinic end generated code: output=873a24faf02e848a input=3113f8e7038b2f6c]*/
 {
-    Py_buffer vbuf;
-    PyObject *result;
-    PyStructObject *soself = (PyStructObject *)self;
-
-    assert(PyStruct_Check(self));
-    assert(soself->s_codes != NULL);
-    if (PyObject_GetBuffer(input, &vbuf, PyBUF_SIMPLE) < 0)
-        return NULL;
-    if (vbuf.len != soself->s_size) {
+    assert(self->s_codes != NULL);
+    if (buffer->len != self->s_size) {
         PyErr_Format(StructError,
                      "unpack requires a bytes object of length %zd",
-                     soself->s_size);
-        PyBuffer_Release(&vbuf);
+                     self->s_size);
         return NULL;
     }
-    result = s_unpack_internal(soself, vbuf.buf);
-    PyBuffer_Release(&vbuf);
-    return result;
+    return s_unpack_internal(self, buffer->buf);
 }
 
-PyDoc_STRVAR(s_unpack_from__doc__,
-"S.unpack_from(buffer, offset=0) -> (v1, v2, ...)\n\
-\n\
-Return a tuple containing values unpacked according to the format\n\
-string S.format.  The buffer's size in bytes, minus offset, must be at\n\
-least S.size.  See help(struct) for more on format strings.");
+/*[clinic input]
+Struct.unpack_from
+
+    buffer: Py_buffer
+    offset: Py_ssize_t = 0
+
+Return a tuple containing unpacked values.
+
+Values are unpacked according to the format string Struct.format.
+
+The buffer's size in bytes, minus offset, must be at least Struct.size.
+
+See help(struct) for more on format strings.
+[clinic start generated code]*/
 
 static PyObject *
-s_unpack_from(PyObject *self, PyObject *args, PyObject *kwds)
+Struct_unpack_from_impl(PyStructObject *self, Py_buffer *buffer,
+                        Py_ssize_t offset)
+/*[clinic end generated code: output=57fac875e0977316 input=97ade52422f8962f]*/
 {
-    static char *kwlist[] = {"buffer", "offset", 0};
+    assert(self->s_codes != NULL);
 
-    PyObject *input;
-    Py_ssize_t offset = 0;
-    Py_buffer vbuf;
-    PyObject *result;
-    PyStructObject *soself = (PyStructObject *)self;
-
-    assert(PyStruct_Check(self));
-    assert(soself->s_codes != NULL);
-
-    if (!PyArg_ParseTupleAndKeywords(args, kwds,
-                                     "O|n:unpack_from", kwlist,
-                                     &input, &offset))
-        return NULL;
-    if (PyObject_GetBuffer(input, &vbuf, PyBUF_SIMPLE) < 0)
-        return NULL;
     if (offset < 0)
-        offset += vbuf.len;
-    if (offset < 0 || vbuf.len - offset < soself->s_size) {
+        offset += buffer->len;
+    if (offset < 0 || buffer->len - offset < self->s_size) {
         PyErr_Format(StructError,
             "unpack_from requires a buffer of at least %zd bytes",
-            soself->s_size);
-        PyBuffer_Release(&vbuf);
+            self->s_size);
         return NULL;
     }
-    result = s_unpack_internal(soself, (char*)vbuf.buf + offset);
-    PyBuffer_Release(&vbuf);
-    return result;
+    return s_unpack_internal(self, (char*)buffer->buf + offset);
 }
 
 
+
 /* Unpack iterator type */
 
 typedef struct {
@@ -1680,48 +1683,54 @@
     unpackiter_methods                          /* tp_methods */
 };
 
-PyDoc_STRVAR(s_iter_unpack__doc__,
-"S.iter_unpack(buffer) -> iterator(v1, v2, ...)\n\
-\n\
-Return an iterator yielding tuples unpacked from the given bytes\n\
-source, like a repeated invocation of unpack_from().  Requires\n\
-that the bytes length be a multiple of the struct size.");
+/*[clinic input]
+Struct.iter_unpack
+
+    buffer: object
+    /
+
+Return an iterator yielding tuples.
+
+Tuples are unpacked from the given bytes source, like a repeated
+invocation of unpack_from().
+
+Requires that the bytes length be a multiple of the struct size.
+[clinic start generated code]*/
 
 static PyObject *
-s_iter_unpack(PyObject *_so, PyObject *input)
+Struct_iter_unpack(PyStructObject *self, PyObject *buffer)
+/*[clinic end generated code: output=172d83d0cd15dbab input=6d65b3f3107dbc99]*/
 {
-    PyStructObject *so = (PyStructObject *) _so;
-    unpackiterobject *self;
+    unpackiterobject *iter;
 
-    assert(PyStruct_Check(_so));
-    assert(so->s_codes != NULL);
+    assert(self->s_codes != NULL);
 
-    if (so->s_size == 0) {
+    if (self->s_size == 0) {
         PyErr_Format(StructError,
                      "cannot iteratively unpack with a struct of length 0");
         return NULL;
     }
 
-    self = (unpackiterobject *) PyType_GenericAlloc(&unpackiter_type, 0);
-    if (self == NULL)
+    iter = (unpackiterobject *) PyType_GenericAlloc(&unpackiter_type, 0);
+    if (iter == NULL)
         return NULL;
 
-    if (PyObject_GetBuffer(input, &self->buf, PyBUF_SIMPLE) < 0) {
-        Py_DECREF(self);
+    if (PyObject_GetBuffer(buffer, &iter->buf, PyBUF_SIMPLE) < 0) {
+        Py_DECREF(iter);
         return NULL;
     }
-    if (self->buf.len % so->s_size != 0) {
+    if (iter->buf.len % self->s_size != 0) {
         PyErr_Format(StructError,
                      "iterative unpacking requires a bytes length "
                      "multiple of %zd",
-                     so->s_size);
-        Py_DECREF(self);
+                     self->s_size);
+        Py_DECREF(iter);
         return NULL;
     }
-    Py_INCREF(so);
-    self->so = so;
-    self->index = 0;
-    return (PyObject *) self;
+    Py_INCREF(self);
+    iter->so = self;
+    iter->index = 0;
+    return (PyObject *)iter;
 }
 
 
@@ -1736,7 +1745,7 @@
  *
  */
 static int
-s_pack_internal(PyStructObject *soself, PyObject *args, int offset, char* buf)
+s_pack_internal(PyStructObject *soself, PyObject **args, int offset, char* buf)
 {
     formatcode *code;
     /* XXX(nnorwitz): why does i need to be a local?  can we use
@@ -1750,7 +1759,7 @@
         char *res = buf + code->offset;
         Py_ssize_t j = code->repeat;
         while (j--) {
-            PyObject *v = PyTuple_GET_ITEM(args, i++);
+            PyObject *v = args[i++];
             if (e->format == 's') {
                 Py_ssize_t n;
                 int isstring;
@@ -1823,7 +1832,7 @@
 strings.");
 
 static PyObject *
-s_pack(PyObject *self, PyObject *args)
+s_pack(PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
 {
     PyStructObject *soself;
     PyObject *result;
@@ -1832,10 +1841,13 @@
     soself = (PyStructObject *)self;
     assert(PyStruct_Check(self));
     assert(soself->s_codes != NULL);
-    if (PyTuple_GET_SIZE(args) != soself->s_len)
+    if (nargs != soself->s_len)
     {
         PyErr_Format(StructError,
-            "pack expected %zd items for packing (got %zd)", soself->s_len, PyTuple_GET_SIZE(args));
+            "pack expected %zd items for packing (got %zd)", soself->s_len, nargs);
+        return NULL;
+    }
+    if (!_PyArg_NoStackKeywords("pack", kwnames)) {
         return NULL;
     }
 
@@ -1862,7 +1874,7 @@
 help(struct) for more on format strings.");
 
 static PyObject *
-s_pack_into(PyObject *self, PyObject *args)
+s_pack_into(PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
 {
     PyStructObject *soself;
     Py_buffer buffer;
@@ -1872,31 +1884,34 @@
     soself = (PyStructObject *)self;
     assert(PyStruct_Check(self));
     assert(soself->s_codes != NULL);
-    if (PyTuple_GET_SIZE(args) != (soself->s_len + 2))
+    if (nargs != (soself->s_len + 2))
     {
-        if (PyTuple_GET_SIZE(args) == 0) {
+        if (nargs == 0) {
             PyErr_Format(StructError,
                         "pack_into expected buffer argument");
         }
-        else if (PyTuple_GET_SIZE(args) == 1) {
+        else if (nargs == 1) {
             PyErr_Format(StructError,
                         "pack_into expected offset argument");
         }
         else {
             PyErr_Format(StructError,
                         "pack_into expected %zd items for packing (got %zd)",
-                        soself->s_len, (PyTuple_GET_SIZE(args) - 2));
+                        soself->s_len, (nargs - 2));
         }
         return NULL;
     }
+    if (!_PyArg_NoStackKeywords("pack_into", kwnames)) {
+        return NULL;
+    }
 
     /* Extract a writable memory buffer from the first argument */
-    if (!PyArg_Parse(PyTuple_GET_ITEM(args, 0), "w*", &buffer))
+    if (!PyArg_Parse(args[0], "w*", &buffer))
         return NULL;
     assert(buffer.len >= 0);
 
     /* Extract the offset from the first argument */
-    offset = PyNumber_AsSsize_t(PyTuple_GET_ITEM(args, 1), PyExc_IndexError);
+    offset = PyNumber_AsSsize_t(args[1], PyExc_IndexError);
     if (offset == -1 && PyErr_Occurred()) {
         PyBuffer_Release(&buffer);
         return NULL;
@@ -1956,22 +1971,15 @@
 /* List of functions */
 
 static struct PyMethodDef s_methods[] = {
-    {"iter_unpack",     s_iter_unpack,  METH_O, s_iter_unpack__doc__},
-    {"pack",            s_pack,         METH_VARARGS, s_pack__doc__},
-    {"pack_into",       s_pack_into,    METH_VARARGS, s_pack_into__doc__},
-    {"unpack",          s_unpack,       METH_O, s_unpack__doc__},
-    {"unpack_from",     (PyCFunction)s_unpack_from, METH_VARARGS|METH_KEYWORDS,
-                    s_unpack_from__doc__},
+    STRUCT_ITER_UNPACK_METHODDEF
+    {"pack",            (PyCFunction)s_pack, METH_FASTCALL, s_pack__doc__},
+    {"pack_into",       (PyCFunction)s_pack_into, METH_FASTCALL, s_pack_into__doc__},
+    STRUCT_UNPACK_METHODDEF
+    STRUCT_UNPACK_FROM_METHODDEF
     {"__sizeof__",      (PyCFunction)s_sizeof, METH_NOARGS, s_sizeof__doc__},
     {NULL,       NULL}          /* sentinel */
 };
 
-PyDoc_STRVAR(s__doc__,
-"Struct(fmt) --> compiled struct object\n"
-"\n"
-"Return a new Struct object which writes and reads binary data according to\n"
-"the format string fmt.  See help(struct) for more on format strings.");
-
 #define OFF(x) offsetof(PyStructObject, x)
 
 static PyGetSetDef s_getsetlist[] = {
@@ -1998,29 +2006,29 @@
     0,                                          /* tp_hash */
     0,                                          /* tp_call */
     0,                                          /* tp_str */
-    PyObject_GenericGetAttr,            /* tp_getattro */
-    PyObject_GenericSetAttr,            /* tp_setattro */
+    PyObject_GenericGetAttr,                    /* tp_getattro */
+    PyObject_GenericSetAttr,                    /* tp_setattro */
     0,                                          /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
-    s__doc__,                           /* tp_doc */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /* tp_flags */
+    Struct___init____doc__,                     /* tp_doc */
     0,                                          /* tp_traverse */
     0,                                          /* tp_clear */
     0,                                          /* tp_richcompare */
     offsetof(PyStructObject, weakreflist),      /* tp_weaklistoffset */
     0,                                          /* tp_iter */
     0,                                          /* tp_iternext */
-    s_methods,                          /* tp_methods */
-    NULL,                               /* tp_members */
-    s_getsetlist,               /* tp_getset */
+    s_methods,                                  /* tp_methods */
+    NULL,                                       /* tp_members */
+    s_getsetlist,                               /* tp_getset */
     0,                                          /* tp_base */
     0,                                          /* tp_dict */
     0,                                          /* tp_descr_get */
     0,                                          /* tp_descr_set */
     0,                                          /* tp_dictoffset */
-    s_init,                             /* tp_init */
-    PyType_GenericAlloc,/* tp_alloc */
-    s_new,                              /* tp_new */
-    PyObject_Del,               /* tp_free */
+    Struct___init__,                            /* tp_init */
+    PyType_GenericAlloc,                        /* tp_alloc */
+    s_new,                                      /* tp_new */
+    PyObject_Del,                               /* tp_free */
 };
 
 
@@ -2029,7 +2037,7 @@
 #define MAXCACHE 100
 static PyObject *cache = NULL;
 
-static PyObject *
+static PyStructObject *
 cache_struct(PyObject *fmt)
 {
     PyObject * s_object;
@@ -2043,7 +2051,7 @@
     s_object = PyDict_GetItem(cache, fmt);
     if (s_object != NULL) {
         Py_INCREF(s_object);
-        return s_object;
+        return (PyStructObject *)s_object;
     }
 
     s_object = PyObject_CallFunctionObjArgs((PyObject *)(&PyStructType), fmt, NULL);
@@ -2054,191 +2062,203 @@
         if (PyDict_SetItem(cache, fmt, s_object) == -1)
             PyErr_Clear();
     }
-    return s_object;
+    return (PyStructObject *)s_object;
 }
 
-PyDoc_STRVAR(clearcache_doc,
-"Clear the internal cache.");
+/*[clinic input]
+_clearcache
+
+Clear the internal cache.
+[clinic start generated code]*/
 
 static PyObject *
-clearcache(PyObject *self)
+_clearcache_impl(PyObject *module)
+/*[clinic end generated code: output=ce4fb8a7bf7cb523 input=463eaae04bab3211]*/
 {
     Py_CLEAR(cache);
     Py_RETURN_NONE;
 }
 
-PyDoc_STRVAR(calcsize_doc,
-"calcsize(fmt) -> integer\n\
-\n\
-Return size in bytes of the struct described by the format string fmt.");
+
+/*[clinic input]
+calcsize
+
+    format: object
+    /
+
+Return size in bytes of the struct described by the format string.
+[clinic start generated code]*/
 
 static PyObject *
-calcsize(PyObject *self, PyObject *fmt)
+calcsize(PyObject *module, PyObject *format)
+/*[clinic end generated code: output=90fbcf191fe9470a input=55488303a06777fa]*/
 {
     Py_ssize_t n;
-    PyObject *s_object = cache_struct(fmt);
+    PyStructObject *s_object = cache_struct(format);
     if (s_object == NULL)
         return NULL;
-    n = ((PyStructObject *)s_object)->s_size;
+    n = s_object->s_size;
     Py_DECREF(s_object);
     return PyLong_FromSsize_t(n);
 }
 
 PyDoc_STRVAR(pack_doc,
-"pack(fmt, v1, v2, ...) -> bytes\n\
+"pack(format, v1, v2, ...) -> bytes\n\
 \n\
 Return a bytes object containing the values v1, v2, ... packed according\n\
-to the format string fmt.  See help(struct) for more on format strings.");
+to the format string.  See help(struct) for more on format strings.");
 
 static PyObject *
-pack(PyObject *self, PyObject *args)
+pack(PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
 {
-    PyObject *s_object, *fmt, *newargs, *result;
-    Py_ssize_t n = PyTuple_GET_SIZE(args);
+    PyStructObject *s_object;
+    PyObject *format, *result;
 
-    if (n == 0) {
+    if (nargs == 0) {
         PyErr_SetString(PyExc_TypeError, "missing format argument");
         return NULL;
     }
-    fmt = PyTuple_GET_ITEM(args, 0);
-    newargs = PyTuple_GetSlice(args, 1, n);
-    if (newargs == NULL)
-        return NULL;
+    format = args[0];
 
-    s_object = cache_struct(fmt);
+    s_object = cache_struct(format);
     if (s_object == NULL) {
-        Py_DECREF(newargs);
         return NULL;
     }
-    result = s_pack(s_object, newargs);
-    Py_DECREF(newargs);
+    result = s_pack((PyObject *)s_object, args + 1, nargs - 1, kwnames);
     Py_DECREF(s_object);
     return result;
 }
 
 PyDoc_STRVAR(pack_into_doc,
-"pack_into(fmt, buffer, offset, v1, v2, ...)\n\
+"pack_into(format, buffer, offset, v1, v2, ...)\n\
 \n\
-Pack the values v1, v2, ... according to the format string fmt and write\n\
+Pack the values v1, v2, ... according to the format string and write\n\
 the packed bytes into the writable buffer buf starting at offset.  Note\n\
 that the offset is a required argument.  See help(struct) for more\n\
 on format strings.");
 
 static PyObject *
-pack_into(PyObject *self, PyObject *args)
+pack_into(PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
 {
-    PyObject *s_object, *fmt, *newargs, *result;
-    Py_ssize_t n = PyTuple_GET_SIZE(args);
+    PyStructObject *s_object;
+    PyObject *format, *result;
 
-    if (n == 0) {
+    if (nargs == 0) {
         PyErr_SetString(PyExc_TypeError, "missing format argument");
         return NULL;
     }
-    fmt = PyTuple_GET_ITEM(args, 0);
-    newargs = PyTuple_GetSlice(args, 1, n);
-    if (newargs == NULL)
-        return NULL;
+    format = args[0];
 
-    s_object = cache_struct(fmt);
+    s_object = cache_struct(format);
     if (s_object == NULL) {
-        Py_DECREF(newargs);
         return NULL;
     }
-    result = s_pack_into(s_object, newargs);
-    Py_DECREF(newargs);
+    result = s_pack_into((PyObject *)s_object, args + 1, nargs - 1, kwnames);
     Py_DECREF(s_object);
     return result;
 }
 
-PyDoc_STRVAR(unpack_doc,
-"unpack(fmt, buffer) -> (v1, v2, ...)\n\
-\n\
-Return a tuple containing values unpacked according to the format string\n\
-fmt.  The buffer's size in bytes must be calcsize(fmt). See help(struct)\n\
-for more on format strings.");
+/*[clinic input]
+unpack
+
+    format: object
+    inputstr: object
+    /
+
+Return a tuple containing values unpacked according to the format string.
+
+The buffer's size in bytes must be calcsize(format).
+
+See help(struct) for more on format strings.
+[clinic start generated code]*/
 
 static PyObject *
-unpack(PyObject *self, PyObject *args)
+unpack_impl(PyObject *module, PyObject *format, PyObject *inputstr)
+/*[clinic end generated code: output=06951d66eae6d63b input=4b81d54988890f5e]*/
 {
-    PyObject *s_object, *fmt, *inputstr, *result;
+    PyStructObject *s_object;
+    PyObject *result;
 
-    if (!PyArg_UnpackTuple(args, "unpack", 2, 2, &fmt, &inputstr))
-        return NULL;
-
-    s_object = cache_struct(fmt);
+    s_object = cache_struct(format);
     if (s_object == NULL)
         return NULL;
-    result = s_unpack(s_object, inputstr);
+    result = Struct_unpack(s_object, inputstr);
     Py_DECREF(s_object);
     return result;
 }
 
-PyDoc_STRVAR(unpack_from_doc,
-"unpack_from(fmt, buffer, offset=0) -> (v1, v2, ...)\n\
-\n\
-Return a tuple containing values unpacked according to the format string\n\
-fmt.  The buffer's size, minus offset, must be at least calcsize(fmt).\n\
-See help(struct) for more on format strings.");
+/*[clinic input]
+unpack_from
+
+    format: object
+    /
+    buffer: Py_buffer
+    offset: Py_ssize_t = 0
+
+Return a tuple containing values unpacked according to the format string.
+
+The buffer's size, minus offset, must be at least calcsize(format).
+
+See help(struct) for more on format strings.
+[clinic start generated code]*/
 
 static PyObject *
-unpack_from(PyObject *self, PyObject *args, PyObject *kwds)
+unpack_from_impl(PyObject *module, PyObject *format, Py_buffer *buffer,
+                 Py_ssize_t offset)
+/*[clinic end generated code: output=2492f0c3a0b82577 input=9ead76c6ac7164f7]*/
 {
-    PyObject *s_object, *fmt, *newargs, *result;
-    Py_ssize_t n = PyTuple_GET_SIZE(args);
+    PyStructObject *s_object;
+    PyObject *result;
 
-    if (n == 0) {
-        PyErr_SetString(PyExc_TypeError, "missing format argument");
-        return NULL;
-    }
-    fmt = PyTuple_GET_ITEM(args, 0);
-    newargs = PyTuple_GetSlice(args, 1, n);
-    if (newargs == NULL)
-        return NULL;
-
-    s_object = cache_struct(fmt);
+    s_object = cache_struct(format);
     if (s_object == NULL) {
-        Py_DECREF(newargs);
         return NULL;
     }
-    result = s_unpack_from(s_object, newargs, kwds);
-    Py_DECREF(newargs);
+    result = Struct_unpack_from_impl(s_object, buffer, offset);
+
     Py_DECREF(s_object);
     return result;
 }
 
-PyDoc_STRVAR(iter_unpack_doc,
-"iter_unpack(fmt, buffer) -> iterator(v1, v2, ...)\n\
-\n\
-Return an iterator yielding tuples unpacked from the given bytes\n\
-source according to the format string, like a repeated invocation of\n\
-unpack_from().  Requires that the bytes length be a multiple of the\n\
-format struct size.");
+/*[clinic input]
+iter_unpack
+
+    format: object
+    buffer: object
+    /
+
+Return an iterator yielding tuples unpacked from the given bytes.
+
+The bytes are unpacked according to the format string, like
+a repeated invocation of unpack_from().
+
+Requires that the bytes length be a multiple of the format struct size.
+[clinic start generated code]*/
 
 static PyObject *
-iter_unpack(PyObject *self, PyObject *args)
+iter_unpack_impl(PyObject *module, PyObject *format, PyObject *buffer)
+/*[clinic end generated code: output=b1291e97a6d4cf3c input=8674dfd2f0dae416]*/
 {
-    PyObject *s_object, *fmt, *input, *result;
+    PyStructObject *s_object;
+    PyObject *result;
 
-    if (!PyArg_ParseTuple(args, "OO:iter_unpack", &fmt, &input))
-        return NULL;
-
-    s_object = cache_struct(fmt);
+    s_object = cache_struct(format);
     if (s_object == NULL)
         return NULL;
-    result = s_iter_unpack(s_object, input);
+
+    result = Struct_iter_unpack(s_object, buffer);
     Py_DECREF(s_object);
     return result;
 }
 
 static struct PyMethodDef module_functions[] = {
-    {"_clearcache",     (PyCFunction)clearcache,        METH_NOARGS,    clearcache_doc},
-    {"calcsize",        calcsize,       METH_O, calcsize_doc},
-    {"iter_unpack",     iter_unpack,    METH_VARARGS,   iter_unpack_doc},
-    {"pack",            pack,           METH_VARARGS,   pack_doc},
-    {"pack_into",       pack_into,      METH_VARARGS,   pack_into_doc},
-    {"unpack",          unpack, METH_VARARGS,   unpack_doc},
-    {"unpack_from",     (PyCFunction)unpack_from,
-                    METH_VARARGS|METH_KEYWORDS,         unpack_from_doc},
+    _CLEARCACHE_METHODDEF
+    CALCSIZE_METHODDEF
+    ITER_UNPACK_METHODDEF
+    {"pack",            (PyCFunction)pack, METH_FASTCALL,   pack_doc},
+    {"pack_into",       (PyCFunction)pack_into, METH_FASTCALL,   pack_into_doc},
+    UNPACK_METHODDEF
+    UNPACK_FROM_METHODDEF
     {NULL,       NULL}          /* sentinel */
 };