diff --git a/LICENSE b/LICENSE index dac2da44..81864d90 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,6 @@ +------------------------------------------------------------------------------- PyCUDA is licensed to you under the MIT/X Consortium license: +------------------------------------------------------------------------------- Copyright (c) 2009,10 Andreas Klöckner and Contributors. @@ -23,9 +25,11 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------- PyCUDA includes derivatives of parts of the `Thrust `_ computing package (in particular the scan implementation). These parts are licensed as follows: +------------------------------------------------------------------------------- Copyright 2008-2011 NVIDIA Corporation @@ -41,3 +45,53 @@ implementation). These parts are licensed as follows: See the License for the specific language governing permissions and limitations under the License. +------------------------------------------------------------------------------- +PyCUDA includes a modified version of the _struct module from CPython: +------------------------------------------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + diff --git a/setup.py b/setup.py index 9adccd87..b655b209 100644 --- a/setup.py +++ b/setup.py @@ -186,11 +186,6 @@ def main(): import sys - if sys.version_info >= (3,): - pvt_struct_source = "src/wrapper/_pvt_struct_v3.cpp" - else: - pvt_struct_source = "src/wrapper/_pvt_struct_v2.cpp" - setup( name="pycuda", # metadata @@ -254,7 +249,7 @@ def main(): ), ExtensionUsingNumpy( "_pvt_struct", - [pvt_struct_source], + ["src/wrapper/_pvt_struct_v3.cpp"], extra_compile_args=conf["CXXFLAGS"], extra_link_args=conf["LDFLAGS"], ), diff --git a/src/wrapper/_pvt_struct_v2.cpp b/src/wrapper/_pvt_struct_v2.cpp deleted file mode 100644 index aab88150..00000000 --- a/src/wrapper/_pvt_struct_v2.cpp +++ /dev/null @@ -1,1617 +0,0 @@ -/* struct module -- pack values into and (out of) strings */ - -/* New version supporting byte order, alignment and size options, - character strings, and unsigned numbers */ - -/* Compared with vanilla Python's struct module, this adds support - * for packing complex values and only supports native packing. - * (the minimum that's needed for PyOpenCL/PyCUDA.) */ - -#define PY_ARRAY_UNIQUE_SYMBOL pycuda_pvt_struct_v2_ARRAY_API - -#define PY_SSIZE_T_CLEAN - -#include "Python.h" -#include -#include "structseq.h" -#include "structmember.h" -#include - -// static PyTypeObject PyStructType; - -/* compatibility macros */ -#if (PY_VERSION_HEX < 0x02050000) -#ifndef PY_SSIZE_T_MIN -typedef long int Py_ssize_t; -#endif - -#define PyInt_FromSsize_t(x) PyInt_FromLong(x) -#define PyInt_AsSsize_t(x) PyInt_AsLong(x) -#endif - -/* If PY_STRUCT_FLOAT_COERCE is defined, the struct module will allow float - arguments for integer formats with a warning for backwards - compatibility. */ - -#define PY_STRUCT_FLOAT_COERCE 1 - -#ifdef PY_STRUCT_FLOAT_COERCE -#define FLOAT_COERCE "integer argument expected, got float" -#endif - -/* Compatibility with Py2.5 and older */ - -#ifndef Py_TYPE -# define Py_TYPE(o) ((o)->ob_type) -#endif - -#ifndef PyVarObject_HEAD_INIT -#define PyVarObject_HEAD_INIT(type, size) \ - PyObject_HEAD_INIT(type) size, -#endif - -#ifndef SIZEOF_SIZE_T -#define SIZEOF_SIZE_T sizeof(size_t) -#endif - -#ifndef PY_SSIZE_T_MAX -#define PY_SSIZE_T_MAX LONG_MAX -#endif - -/* The translation function for each format character is table driven */ -typedef struct _formatdef { - char format; - Py_ssize_t size; - Py_ssize_t alignment; - PyObject* (*unpack)(const char *, - const struct _formatdef *); - int (*pack)(char *, PyObject *, - const struct _formatdef *); -} formatdef; - -typedef struct _formatcode { - const struct _formatdef *fmtdef; - Py_ssize_t offset; - Py_ssize_t size; -} formatcode; - -/* Struct object interface */ - -typedef struct { - PyObject_HEAD - Py_ssize_t s_size; - Py_ssize_t s_len; - formatcode *s_codes; - PyObject *s_format; - PyObject *weakreflist; /* List of weak references */ -} PyStructObject; - - -#define PyStruct_Check(op) PyObject_TypeCheck(op, &PyStructType) -#define PyStruct_CheckExact(op) (Py_TYPE(op) == &PyStructType) - - -/* Exception */ - -static PyObject *StructError; - - -/* Define various structs to figure out the alignments of types */ - - -typedef struct { char c; short x; } st_short; -typedef struct { char c; int x; } st_int; -typedef struct { char c; long x; } st_long; -typedef struct { char c; float x; } st_float; -typedef struct { char c; double x; } st_double; -typedef struct { char c; void *x; } st_void_p; - -#define SHORT_ALIGN (sizeof(st_short) - sizeof(short)) -#define INT_ALIGN (sizeof(st_int) - sizeof(int)) -#define LONG_ALIGN (sizeof(st_long) - sizeof(long)) -#define FLOAT_ALIGN (sizeof(st_float) - sizeof(float)) -#define DOUBLE_ALIGN (sizeof(st_double) - sizeof(double)) -#define VOID_P_ALIGN (sizeof(st_void_p) - sizeof(void *)) - -/* We can't support q and Q in native mode unless the compiler does; - in std mode, they're 8 bytes on all platforms. */ -#ifdef HAVE_LONG_LONG -typedef struct { char c; PY_LONG_LONG x; } s_long_long; -#define LONG_LONG_ALIGN (sizeof(s_long_long) - sizeof(PY_LONG_LONG)) -#endif - -#define BOOL_TYPE bool -typedef struct { char c; bool x; } s_bool; -#define BOOL_ALIGN (sizeof(s_bool) - sizeof(BOOL_TYPE)) - -#define STRINGIFY(x) #x - -#ifdef __powerc -#pragma options align=reset -#endif - -static char *integer_codes = "bBhHiIlLqQ"; - -static void s_dealloc(PyStructObject *s); -static int s_init(PyObject *self, PyObject *args, PyObject *kwds); -static PyObject *s_new(PyTypeObject *type, PyObject *args, PyObject *kwds); -static PyObject *s_pack(PyObject *self, PyObject *args); -static PyObject *s_pack_into(PyObject *self, PyObject *args); -static PyObject *s_unpack(PyObject *self, PyObject *inputstr); -static PyObject *s_unpack_from(PyObject *self, PyObject *args, PyObject *kwds); -static PyObject *s_get_format(PyStructObject *self, void *unused); -static PyObject *s_get_size(PyStructObject *self, void *unused); - -PyDoc_STRVAR(s__doc__, "Compiled struct object"); - -/* List of functions */ - -PyDoc_STRVAR(s_pack__doc__, -"S.pack(v1, v2, ...) -> string\n\ -\n\ -Return a string containing values v1, v2, ... packed according to this\n\ -Struct's format. See struct.__doc__ for more on format strings."); - -PyDoc_STRVAR(s_pack_into__doc__, -"S.pack_into(buffer, offset, v1, v2, ...)\n\ -\n\ -Pack the values v1, v2, ... according to this Struct's format, write \n\ -the packed bytes into the writable buffer buf starting at offset. Note\n\ -that the offset is not an optional argument. See struct.__doc__ for \n\ -more on format strings."); - -PyDoc_STRVAR(s_unpack__doc__, -"S.unpack(str) -> (v1, v2, ...)\n\ -\n\ -Return tuple containing values unpacked according to this Struct's format.\n\ -Requires len(str) == self.size. See struct.__doc__ for more on format\n\ -strings."); - -PyDoc_STRVAR(s_unpack_from__doc__, -"S.unpack_from(buffer[, offset]) -> (v1, v2, ...)\n\ -\n\ -Return tuple containing values unpacked according to this Struct's format.\n\ -Unlike unpack, unpack_from can unpack values from any object supporting\n\ -the buffer API, not just str. Requires len(buffer[offset:]) >= self.size.\n\ -See struct.__doc__ for more on format strings."); - - -static struct PyMethodDef s_methods[] = { - {"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__}, - {NULL, NULL} /* sentinel */ -}; - -#define OFF(x) offsetof(PyStructObject, x) - -static PyGetSetDef s_getsetlist[] = { - {"format", (getter)s_get_format, (setter)NULL, "struct format string", NULL}, - {"size", (getter)s_get_size, (setter)NULL, "struct size in bytes", NULL}, - {NULL} /* sentinel */ -}; - -static -PyTypeObject PyStructType = { - PyVarObject_HEAD_INIT(NULL, 0) - "Struct", - sizeof(PyStructObject), - 0, - (destructor)s_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - PyObject_GenericSetAttr, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_WEAKREFS,/* tp_flags */ - s__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 */ - 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 */ -}; - -/* Helper to get a PyLongObject by hook or by crook. Caller should decref. */ - -static PyObject * -get_pylong(PyObject *v) -{ - PyNumberMethods *m; - - assert(v != NULL); - if (PyInt_Check(v)) - return PyLong_FromLong(PyInt_AS_LONG(v)); - if (PyLong_Check(v)) { - Py_INCREF(v); - return v; - } - - m = Py_TYPE(v)->tp_as_number; - if (m != NULL && m->nb_long != NULL) { - v = m->nb_long(v); - if (v == NULL) - return NULL; - if (PyLong_Check(v)) - return v; - Py_DECREF(v); - } - - PyErr_SetString(StructError, - "cannot convert argument to long"); - return NULL; -} - -/* Helper to convert a Python object to a C long. Sets an exception - (struct.error for an inconvertible type, OverflowError for - out-of-range values) and returns -1 on error. */ - -static int -get_long(PyObject *v, long *p) -{ - long x; - - v = get_pylong(v); - if (v == NULL) - return -1; - assert(PyLong_Check(v)); - x = PyLong_AsLong(v); - Py_DECREF(v); - if (x == (long)-1 && PyErr_Occurred()) - return -1; - *p = x; - return 0; -} - -/* Same, but handling unsigned long */ - -static int -get_ulong(PyObject *v, unsigned long *p) -{ - unsigned long x; - - v = get_pylong(v); - if (v == NULL) - return -1; - assert(PyLong_Check(v)); - x = PyLong_AsUnsignedLong(v); - Py_DECREF(v); - if (x == (unsigned long)-1 && PyErr_Occurred()) - return -1; - *p = x; - return 0; -} - -#ifdef HAVE_LONG_LONG - -/* Same, but handling native long long. */ - -static int -get_longlong(PyObject *v, PY_LONG_LONG *p) -{ - PY_LONG_LONG x; - - v = get_pylong(v); - if (v == NULL) - return -1; - assert(PyLong_Check(v)); - x = PyLong_AsLongLong(v); - Py_DECREF(v); - if (x == (PY_LONG_LONG)-1 && PyErr_Occurred()) - return -1; - *p = x; - return 0; -} - -/* Same, but handling native unsigned long long. */ - -static int -get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p) -{ - unsigned PY_LONG_LONG x; - - v = get_pylong(v); - if (v == NULL) - return -1; - assert(PyLong_Check(v)); - x = PyLong_AsUnsignedLongLong(v); - Py_DECREF(v); - if (x == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) - return -1; - *p = x; - return 0; -} - -#endif - -#if (SIZEOF_LONG > SIZEOF_INT) - -/* Helper to format the range error exceptions */ -static int -_range_error(const formatdef *f, int is_unsigned) -{ - /* ulargest is the largest unsigned value with f->size bytes. - * Note that the simpler: - * ((size_t)1 << (f->size * 8)) - 1 - * doesn't work when f->size == sizeof(size_t) because C doesn't - * define what happens when a left shift count is >= the number of - * bits in the integer being shifted; e.g., on some boxes it doesn't - * shift at all when they're equal. - */ - const size_t ulargest = (size_t)-1 >> ((SIZEOF_SIZE_T - f->size)*8); - assert(f->size >= 1 && f->size <= SIZEOF_SIZE_T); - if (is_unsigned) - PyErr_Format(StructError, - "'%c' format requires 0 <= number <= %zu", - f->format, - ulargest); - else { - const Py_ssize_t largest = (Py_ssize_t)(ulargest >> 1); - PyErr_Format(StructError, - "'%c' format requires %zd <= number <= %zd", - f->format, - ~ largest, - largest); - } - return -1; -} - -#endif - - -/* A large number of small routines follow, with names of the form - - [bln][up]_TYPE - - [bln] distiguishes among big-endian, little-endian and native. - [pu] distiguishes between pack (to struct) and unpack (from struct). - TYPE is one of char, byte, ubyte, etc. -*/ - -/* Native mode routines. ****************************************************/ -/* NOTE: - In all n[up]_ routines handling types larger than 1 byte, there is - *no* guarantee that the p pointer is properly aligned for each type, - therefore memcpy is called. An intermediate variable is used to - compensate for big-endian architectures. - Normally both the intermediate variable and the memcpy call will be - skipped by C optimisation in little-endian architectures (gcc >= 2.91 - does this). */ - -static PyObject * -nu_char(const char *p, const formatdef *f) -{ - return PyString_FromStringAndSize(p, 1); -} - -static PyObject * -nu_byte(const char *p, const formatdef *f) -{ - return PyInt_FromLong((long) *(signed char *)p); -} - -static PyObject * -nu_ubyte(const char *p, const formatdef *f) -{ - return PyInt_FromLong((long) *(unsigned char *)p); -} - -static PyObject * -nu_short(const char *p, const formatdef *f) -{ - short x; - memcpy((char *)&x, p, sizeof x); - return PyInt_FromLong((long)x); -} - -static PyObject * -nu_ushort(const char *p, const formatdef *f) -{ - unsigned short x; - memcpy((char *)&x, p, sizeof x); - return PyInt_FromLong((long)x); -} - -static PyObject * -nu_int(const char *p, const formatdef *f) -{ - int x; - memcpy((char *)&x, p, sizeof x); - return PyInt_FromLong((long)x); -} - -static PyObject * -nu_uint(const char *p, const formatdef *f) -{ - unsigned int x; - memcpy((char *)&x, p, sizeof x); -#if (SIZEOF_LONG > SIZEOF_INT) - return PyInt_FromLong((long)x); -#else - if (x <= ((unsigned int)LONG_MAX)) - return PyInt_FromLong((long)x); - return PyLong_FromUnsignedLong((unsigned long)x); -#endif -} - -static PyObject * -nu_long(const char *p, const formatdef *f) -{ - long x; - memcpy((char *)&x, p, sizeof x); - return PyInt_FromLong(x); -} - -static PyObject * -nu_ulong(const char *p, const formatdef *f) -{ - unsigned long x; - memcpy((char *)&x, p, sizeof x); - if (x <= LONG_MAX) - return PyInt_FromLong((long)x); - return PyLong_FromUnsignedLong(x); -} - -/* Native mode doesn't support q or Q unless the platform C supports - long long (or, on Windows, __int64). */ - -#ifdef HAVE_LONG_LONG - -static PyObject * -nu_longlong(const char *p, const formatdef *f) -{ - PY_LONG_LONG x; - memcpy((char *)&x, p, sizeof x); - if (x >= LONG_MIN && x <= LONG_MAX) - return PyInt_FromLong(Py_SAFE_DOWNCAST(x, PY_LONG_LONG, long)); - return PyLong_FromLongLong(x); -} - -static PyObject * -nu_ulonglong(const char *p, const formatdef *f) -{ - unsigned PY_LONG_LONG x; - memcpy((char *)&x, p, sizeof x); - if (x <= LONG_MAX) - return PyInt_FromLong(Py_SAFE_DOWNCAST(x, unsigned PY_LONG_LONG, long)); - return PyLong_FromUnsignedLongLong(x); -} - -#endif - -static PyObject * -nu_bool(const char *p, const formatdef *f) -{ - BOOL_TYPE x; - memcpy((char *)&x, p, sizeof x); - return PyBool_FromLong(x != 0); -} - - -static PyObject * -nu_float(const char *p, const formatdef *f) -{ - float x; - memcpy((char *)&x, p, sizeof x); - return PyFloat_FromDouble((double)x); -} - -static PyObject * -nu_double(const char *p, const formatdef *f) -{ - double x; - memcpy((char *)&x, p, sizeof x); - return PyFloat_FromDouble(x); -} - -static PyObject * -nu_complex_float(const char *p, const formatdef *f) -{ - float re, im; - memcpy((char *)&re, p, sizeof re); - memcpy((char *)&im, p+sizeof re, sizeof im); - return PyComplex_FromDoubles((double)re, (double) im); -} - -static PyObject * -nu_complex_double(const char *p, const formatdef *f) -{ - double re, im; - memcpy((char *)&re, p, sizeof re); - memcpy((char *)&im, p+sizeof re, sizeof im); - return PyComplex_FromDoubles(re, im); -} - -static PyObject * -nu_void_p(const char *p, const formatdef *f) -{ - void *x; - memcpy((char *)&x, p, sizeof x); - return PyLong_FromVoidPtr(x); -} - -static int -np_byte(char *p, PyObject *v, const formatdef *f) -{ - long x; - if (get_long(v, &x) < 0) - return -1; - if (x < -128 || x > 127){ - PyErr_SetString(StructError, - "byte format requires -128 <= number <= 127"); - return -1; - } - *p = (char)x; - return 0; -} - -static int -np_ubyte(char *p, PyObject *v, const formatdef *f) -{ - long x; - if (get_long(v, &x) < 0) - return -1; - if (x < 0 || x > 255){ - PyErr_SetString(StructError, - "ubyte format requires 0 <= number <= 255"); - return -1; - } - *p = (char)x; - return 0; -} - -static int -np_char(char *p, PyObject *v, const formatdef *f) -{ - if (!PyString_Check(v) || PyString_Size(v) != 1) { - PyErr_SetString(StructError, - "char format require string of length 1"); - return -1; - } - *p = *PyString_AsString(v); - return 0; -} - -static int -np_short(char *p, PyObject *v, const formatdef *f) -{ - long x; - short y; - if (get_long(v, &x) < 0) - return -1; - if (x < SHRT_MIN || x > SHRT_MAX){ - PyErr_SetString(StructError, - "short format requires " STRINGIFY(SHRT_MIN) - " <= number <= " STRINGIFY(SHRT_MAX)); - return -1; - } - y = (short)x; - memcpy(p, (char *)&y, sizeof y); - return 0; -} - -static int -np_ushort(char *p, PyObject *v, const formatdef *f) -{ - long x; - unsigned short y; - if (get_long(v, &x) < 0) - return -1; - if (x < 0 || x > USHRT_MAX){ - PyErr_SetString(StructError, - "ushort format requires 0 <= number <= " STRINGIFY(USHRT_MAX)); - return -1; - } - y = (unsigned short)x; - memcpy(p, (char *)&y, sizeof y); - return 0; -} - -static int -np_int(char *p, PyObject *v, const formatdef *f) -{ - long x; - int y; - if (get_long(v, &x) < 0) - return -1; -#if (SIZEOF_LONG > SIZEOF_INT) - if ((x < ((long)INT_MIN)) || (x > ((long)INT_MAX))) - return _range_error(f, 0); -#endif - y = (int)x; - memcpy(p, (char *)&y, sizeof y); - return 0; -} - -static int -np_uint(char *p, PyObject *v, const formatdef *f) -{ - unsigned long x; - unsigned int y; - if (get_ulong(v, &x) < 0) - return -1; - y = (unsigned int)x; -#if (SIZEOF_LONG > SIZEOF_INT) - if (x > ((unsigned long)UINT_MAX)) - return _range_error(f, 1); -#endif - memcpy(p, (char *)&y, sizeof y); - return 0; -} - -static int -np_long(char *p, PyObject *v, const formatdef *f) -{ - long x; - if (get_long(v, &x) < 0) - return -1; - memcpy(p, (char *)&x, sizeof x); - return 0; -} - -static int -np_ulong(char *p, PyObject *v, const formatdef *f) -{ - unsigned long x; - if (get_ulong(v, &x) < 0) - return -1; - memcpy(p, (char *)&x, sizeof x); - return 0; -} - -#ifdef HAVE_LONG_LONG - -static int -np_longlong(char *p, PyObject *v, const formatdef *f) -{ - PY_LONG_LONG x; - if (get_longlong(v, &x) < 0) - return -1; - memcpy(p, (char *)&x, sizeof x); - return 0; -} - -static int -np_ulonglong(char *p, PyObject *v, const formatdef *f) -{ - unsigned PY_LONG_LONG x; - if (get_ulonglong(v, &x) < 0) - return -1; - memcpy(p, (char *)&x, sizeof x); - return 0; -} -#endif - - -static int -np_bool(char *p, PyObject *v, const formatdef *f) -{ - BOOL_TYPE y; - y = PyObject_IsTrue(v) != 0; - memcpy(p, (char *)&y, sizeof y); - return 0; -} - -static int -np_float(char *p, PyObject *v, const formatdef *f) -{ - float x = (float)PyFloat_AsDouble(v); - if (x == -1 && PyErr_Occurred()) { - PyErr_SetString(StructError, - "required argument is not a float"); - return -1; - } - memcpy(p, (char *)&x, sizeof x); - return 0; -} - -static int -np_double(char *p, PyObject *v, const formatdef *f) -{ - double x = PyFloat_AsDouble(v); - if (x == -1 && PyErr_Occurred()) { - PyErr_SetString(StructError, - "required argument is not a float"); - return -1; - } - memcpy(p, (char *)&x, sizeof(double)); - return 0; -} - -static int -np_complex_float(char *p, PyObject *v, const formatdef *f) -{ - if (PyArray_IsZeroDim(v)) { - PyObject *v_cast = PyArray_Cast( - reinterpret_cast(v), - NPY_CFLOAT); - if (!v_cast) - return -1; - memcpy(p, PyArray_DATA(v_cast), PyArray_NBYTES(v_cast)); - Py_DECREF(v_cast); - } - else { - float re = 0.0f; - float im = 0.0f; - Py_complex cplx; -#if (PY_VERSION_HEX < 0x02060000) - if (PyComplex_Check(v)) - cplx = PyComplex_AsCComplex(v); - else if (PyObject_HasAttrString(v, "__complex__")) - { - PyObject *v2 = PyObject_CallMethod(v, "__complex__", ""); - cplx = PyComplex_AsCComplex(v2); - Py_DECREF(v2); - } - else - cplx = PyComplex_AsCComplex(v); -#else - cplx = PyComplex_AsCComplex(v); -#endif - if (PyErr_Occurred()) { - PyErr_SetString(StructError, - "required argument is not a complex"); - return -1; - } - - re = (float)cplx.real; - im = (float)cplx.imag; - memcpy(p, (char *)&re, sizeof re); - memcpy(p+sizeof re, (char *)&im, sizeof im); - } - return 0; -} - -static int -np_complex_double(char *p, PyObject *v, const formatdef *f) -{ - if (PyArray_IsZeroDim(v)) { - PyObject *v_cast = PyArray_Cast( - reinterpret_cast(v), - NPY_CDOUBLE); - if (!v_cast) - return -1; - memcpy(p, PyArray_DATA(v_cast), PyArray_NBYTES(v_cast)); - Py_DECREF(v_cast); - } - else { - double re = 0.0; - double im = 0.0; - Py_complex cplx; -#if (PY_VERSION_HEX < 0x02060000) - if (PyComplex_Check(v)) - cplx = PyComplex_AsCComplex(v); - else if (PyObject_HasAttrString(v, "__complex__")) - { - PyObject *v2 = PyObject_CallMethod(v, "__complex__", ""); - cplx = PyComplex_AsCComplex(v2); - Py_DECREF(v2); - } - else - cplx = PyComplex_AsCComplex(v); -#else - cplx = PyComplex_AsCComplex(v); -#endif - if (PyErr_Occurred()) { - PyErr_SetString(StructError, - "required argument is not a complex"); - return -1; - } - re = cplx.real; - im = cplx.imag; - memcpy(p, (char *)&re, sizeof re); - memcpy(p+sizeof re, (char *)&im, sizeof im); - } - return 0; -} - -static int -np_void_p(char *p, PyObject *v, const formatdef *f) -{ - void *x; - - v = get_pylong(v); - if (v == NULL) - return -1; - assert(PyLong_Check(v)); - x = PyLong_AsVoidPtr(v); - Py_DECREF(v); - if (x == NULL && PyErr_Occurred()) - return -1; - memcpy(p, (char *)&x, sizeof x); - return 0; -} - -static formatdef native_table[] = { - {'x', sizeof(char), 0, NULL}, - {'b', sizeof(char), 0, nu_byte, np_byte}, - {'B', sizeof(char), 0, nu_ubyte, np_ubyte}, - {'c', sizeof(char), 0, nu_char, np_char}, - {'s', sizeof(char), 0, NULL}, - {'p', sizeof(char), 0, NULL}, - {'h', sizeof(short), SHORT_ALIGN, nu_short, np_short}, - {'H', sizeof(short), SHORT_ALIGN, nu_ushort, np_ushort}, - {'i', sizeof(int), INT_ALIGN, nu_int, np_int}, - {'I', sizeof(int), INT_ALIGN, nu_uint, np_uint}, - {'l', sizeof(long), LONG_ALIGN, nu_long, np_long}, - {'L', sizeof(long), LONG_ALIGN, nu_ulong, np_ulong}, -#ifdef HAVE_LONG_LONG - {'q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_longlong, np_longlong}, - {'Q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_ulonglong,np_ulonglong}, -#endif - {'?', sizeof(BOOL_TYPE), BOOL_ALIGN, nu_bool, np_bool}, - {'f', sizeof(float), FLOAT_ALIGN, nu_float, np_float}, - {'d', sizeof(double), DOUBLE_ALIGN, nu_double, np_double}, - {'F', 2*sizeof(float), FLOAT_ALIGN, nu_complex_float, np_complex_float}, - {'D', 2*sizeof(double), DOUBLE_ALIGN, nu_complex_double, np_complex_double}, - {'P', sizeof(void *), VOID_P_ALIGN, nu_void_p, np_void_p}, - {0} -}; - -/* Get the table entry for a format code */ - -static const formatdef * -getentry(int c, const formatdef *f) -{ - for (; f->format != '\0'; f++) { - if (f->format == c) { - return f; - } - } - PyErr_SetString(StructError, "bad char in struct format"); - return NULL; -} - - -/* Align a size according to a format code */ - -static Py_ssize_t -align(Py_ssize_t size, char c, const formatdef *e) -{ - if (e->format == c) { - if (e->alignment) { - size = ((size + e->alignment - 1) - / e->alignment) - * e->alignment; - } - } - return size; -} - - -/* calculate the size of a format string */ - -static int -prepare_s(PyStructObject *self) -{ - const formatdef *f; - const formatdef *e; - formatcode *codes; - - const char *s; - const char *fmt; - char c; - Py_ssize_t size, len, num, itemsize, x; - - fmt = PyString_AS_STRING(self->s_format); - - f = native_table; - - s = fmt; - size = 0; - len = 0; - while ((c = *s++) != '\0') { - if (isspace(Py_CHARMASK(c))) - continue; - if ('0' <= c && c <= '9') { - num = c - '0'; - while ('0' <= (c = *s++) && c <= '9') { - x = num*10 + (c - '0'); - if (x/10 != num) { - PyErr_SetString( - StructError, - "overflow in item count"); - return -1; - } - num = x; - } - if (c == '\0') - break; - } - else - num = 1; - - e = getentry(c, f); - if (e == NULL) - return -1; - - switch (c) { - case 's': /* fall through */ - case 'p': len++; break; - case 'x': break; - default: len += num; break; - } - - itemsize = e->size; - size = align(size, c, e); - x = num * itemsize; - size += x; - if (x/itemsize != num || size < 0) { - PyErr_SetString(StructError, - "total struct size too long"); - return -1; - } - } - - /* check for overflow */ - if ((len + 1) > (PY_SSIZE_T_MAX / sizeof(formatcode))) { - PyErr_NoMemory(); - return -1; - } - - self->s_size = size; - self->s_len = len; - codes = (formatcode *) PyMem_MALLOC((len + 1) * sizeof(formatcode)); - if (codes == NULL) { - PyErr_NoMemory(); - return -1; - } - self->s_codes = codes; - - s = fmt; - size = 0; - while ((c = *s++) != '\0') { - if (isspace(Py_CHARMASK(c))) - continue; - if ('0' <= c && c <= '9') { - num = c - '0'; - while ('0' <= (c = *s++) && c <= '9') - num = num*10 + (c - '0'); - if (c == '\0') - break; - } - else - num = 1; - - e = getentry(c, f); - - size = align(size, c, e); - if (c == 's' || c == 'p') { - codes->offset = size; - codes->size = num; - codes->fmtdef = e; - codes++; - size += num; - } else if (c == 'x') { - size += num; - } else { - while (--num >= 0) { - codes->offset = size; - codes->size = e->size; - codes->fmtdef = e; - codes++; - size += e->size; - } - } - } - codes->fmtdef = NULL; - codes->offset = size; - codes->size = 0; - - return 0; -} - -static PyObject * -s_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *self; - - assert(type != NULL && type->tp_alloc != NULL); - - self = type->tp_alloc(type, 0); - if (self != NULL) { - PyStructObject *s = (PyStructObject*)self; - Py_INCREF(Py_None); - s->s_format = Py_None; - s->s_codes = NULL; - s->s_size = -1; - s->s_len = -1; - } - return self; -} - -static int -s_init(PyObject *self, PyObject *args, PyObject *kwds) -{ - 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, "S:Struct", kwlist, - &o_format)) - return -1; - - Py_INCREF(o_format); - Py_CLEAR(soself->s_format); - soself->s_format = o_format; - - ret = prepare_s(soself); - return ret; -} - -static void -s_dealloc(PyStructObject *s) -{ - if (s->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *)s); - if (s->s_codes != NULL) { - PyMem_FREE(s->s_codes); - } - Py_XDECREF(s->s_format); - Py_TYPE(s)->tp_free((PyObject *)s); -} - -static PyObject * -s_unpack_internal(PyStructObject *soself, char *startfrom) { - formatcode *code; - Py_ssize_t i = 0; - PyObject *result = PyTuple_New(soself->s_len); - if (result == NULL) - return NULL; - - for (code = soself->s_codes; code->fmtdef != NULL; code++) { - PyObject *v; - const formatdef *e = code->fmtdef; - const char *res = startfrom + code->offset; - if (e->format == 's') { - v = PyString_FromStringAndSize(res, code->size); - } else if (e->format == 'p') { - Py_ssize_t n = *(unsigned char*)res; - if (n >= code->size) - n = code->size - 1; - v = PyString_FromStringAndSize(res + 1, n); - } else { - v = e->unpack(res, e); - } - if (v == NULL) - goto fail; - PyTuple_SET_ITEM(result, i++, v); - } - - return result; -fail: - Py_DECREF(result); - return NULL; -} - - -static PyObject * -s_unpack(PyObject *self, PyObject *inputstr) -{ - char *start; - Py_ssize_t len; - PyObject *args=NULL, *result; - PyStructObject *soself = (PyStructObject *)self; - assert(PyStruct_Check(self)); - assert(soself->s_codes != NULL); - if (inputstr == NULL) - goto fail; - if (PyString_Check(inputstr) && - PyString_GET_SIZE(inputstr) == soself->s_size) { - return s_unpack_internal(soself, PyString_AS_STRING(inputstr)); - } - args = PyTuple_Pack(1, inputstr); - if (args == NULL) - return NULL; - if (!PyArg_ParseTuple(args, "s#:unpack", &start, &len)) - goto fail; - if (soself->s_size != len) - goto fail; - result = s_unpack_internal(soself, start); - Py_DECREF(args); - return result; - -fail: - Py_XDECREF(args); - PyErr_Format(StructError, - "unpack requires a string argument of length %zd", - soself->s_size); - return NULL; -} - -static PyObject * -s_unpack_from(PyObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"buffer", "offset", 0}; -#if (PY_VERSION_HEX < 0x02050000) - static char *fmt = "z#|i:unpack_from"; -#else - static char *fmt = "z#|n:unpack_from"; -#endif - Py_ssize_t buffer_len = 0, offset = 0; - char *buffer = NULL; - PyStructObject *soself = (PyStructObject *)self; - assert(PyStruct_Check(self)); - assert(soself->s_codes != NULL); - - if (!PyArg_ParseTupleAndKeywords(args, kwds, fmt, kwlist, - &buffer, &buffer_len, &offset)) - return NULL; - - if (buffer == NULL) { - PyErr_Format(StructError, - "unpack_from requires a buffer argument"); - return NULL; - } - - if (offset < 0) - offset += buffer_len; - - if (offset < 0 || (buffer_len - offset) < soself->s_size) { - PyErr_Format(StructError, - "unpack_from requires a buffer of at least %zd bytes", - soself->s_size); - return NULL; - } - return s_unpack_internal(soself, buffer + offset); -} - - -/* - * Guts of the pack function. - * - * Takes a struct object, a tuple of arguments, and offset in that tuple of - * argument for where to start processing the arguments for packing, and a - * character buffer for writing the packed string. The caller must insure - * that the buffer may contain the required length for packing the arguments. - * 0 is returned on success, 1 is returned if there is an error. - * - */ -static int -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 - the offset parameter or do we need the wider width? */ - Py_ssize_t i; - - memset(buf, '\0', soself->s_size); - i = offset; - for (code = soself->s_codes; code->fmtdef != NULL; code++) { - Py_ssize_t n; - PyObject *v = PyTuple_GET_ITEM(args, i++); - const formatdef *e = code->fmtdef; - char *res = buf + code->offset; - if (e->format == 's') { - if (!PyString_Check(v)) { - if (!PyObject_CheckReadBuffer(v)) - { - PyErr_SetString(StructError, - "argument for 's' must " - "be a string or a buffer"); - return -1; - } - else - { - const void *buf; - Py_ssize_t len; - if (PyObject_AsReadBuffer(v, &buf, &len)) - return -1; - - if (len > code->size) - len = code->size; - if (len > 0) - memcpy(res, buf, len); - } - } - else - { - n = PyString_GET_SIZE(v); - if (n > code->size) - n = code->size; - if (n > 0) - memcpy(res, PyString_AS_STRING(v), n); - } - } else if (e->format == 'p') { - if (!PyString_Check(v)) { - PyErr_SetString(StructError, - "argument for 'p' must " - "be a string"); - return -1; - } - n = PyString_GET_SIZE(v); - if (n > (code->size - 1)) - n = code->size - 1; - if (n > 0) - memcpy(res + 1, PyString_AS_STRING(v), n); - if (n > 255) - n = 255; - *res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char); - } else if (e->pack(res, v, e) < 0) { - if (strchr(integer_codes, e->format) != NULL && - PyErr_ExceptionMatches(PyExc_OverflowError)) - PyErr_Format(StructError, - "integer out of range for " - "'%c' format code", - e->format); - return -1; - } - } - - /* Success */ - return 0; -} - - -static PyObject * -s_pack(PyObject *self, PyObject *args) -{ - PyStructObject *soself; - PyObject *result; - - /* Validate arguments. */ - soself = (PyStructObject *)self; - assert(PyStruct_Check(self)); - assert(soself->s_codes != NULL); - if (PyTuple_GET_SIZE(args) != soself->s_len) - { - PyErr_Format(StructError, - "pack requires exactly %zd arguments", soself->s_len); - return NULL; - } - - /* Allocate a new string */ - result = PyString_FromStringAndSize((char *)NULL, soself->s_size); - if (result == NULL) - return NULL; - - /* Call the guts */ - if ( s_pack_internal(soself, args, 0, PyString_AS_STRING(result)) != 0 ) { - Py_DECREF(result); - return NULL; - } - - return result; -} - - -static PyObject * -s_pack_into(PyObject *self, PyObject *args) -{ - PyStructObject *soself; - char *buffer; - Py_ssize_t buffer_len, offset; - - /* Validate arguments. +1 is for the first arg as buffer. */ - soself = (PyStructObject *)self; - assert(PyStruct_Check(self)); - assert(soself->s_codes != NULL); - if (PyTuple_GET_SIZE(args) != (soself->s_len + 2)) - { - PyErr_Format(StructError, - "pack_into requires exactly %zd arguments", - (soself->s_len + 2)); - return NULL; - } - - /* Extract a writable memory buffer from the first argument */ - if ( PyObject_AsWriteBuffer(PyTuple_GET_ITEM(args, 0), - (void**)&buffer, &buffer_len) == -1 ) { - return NULL; - } - assert( buffer_len >= 0 ); - - /* Extract the offset from the first argument */ - offset = PyInt_AsSsize_t(PyTuple_GET_ITEM(args, 1)); - if (offset == -1 && PyErr_Occurred()) - return NULL; - - /* Support negative offsets. */ - if (offset < 0) - offset += buffer_len; - - /* Check boundaries */ - if (offset < 0 || (buffer_len - offset) < soself->s_size) { - PyErr_Format(StructError, - "pack_into requires a buffer of at least %zd bytes", - soself->s_size); - return NULL; - } - - /* Call the guts */ - if ( s_pack_internal(soself, args, 2, buffer + offset) != 0 ) { - return NULL; - } - - Py_RETURN_NONE; -} - -static PyObject * -s_get_format(PyStructObject *self, void *unused) -{ - Py_INCREF(self->s_format); - return self->s_format; -} - -static PyObject * -s_get_size(PyStructObject *self, void *unused) -{ - return PyInt_FromSsize_t(self->s_size); -} - -/* ---- Standalone functions ---- */ - -#define MAXCACHE 100 -static PyObject *cache = NULL; - -static PyObject * -cache_struct(PyObject *fmt) -{ - PyObject * s_object; - - if (cache == NULL) { - cache = PyDict_New(); - if (cache == NULL) - return NULL; - } - - s_object = PyDict_GetItem(cache, fmt); - if (s_object != NULL) { - Py_INCREF(s_object); - return s_object; - } - - s_object = PyObject_CallFunctionObjArgs((PyObject *)(&PyStructType), fmt, NULL); - if (s_object != NULL) { - if (PyDict_Size(cache) >= MAXCACHE) - PyDict_Clear(cache); - /* Attempt to cache the result */ - if (PyDict_SetItem(cache, fmt, s_object) == -1) - PyErr_Clear(); - } - return s_object; -} - -PyDoc_STRVAR(clearcache_doc, -"Clear the internal cache."); - -static PyObject * -clearcache(PyObject *self) -{ - Py_CLEAR(cache); - Py_RETURN_NONE; -} - -PyDoc_STRVAR(calcsize_doc, -"Return size of C struct described by format string fmt."); - -static PyObject * -calcsize(PyObject *self, PyObject *fmt) -{ - Py_ssize_t n; - PyObject *s_object = cache_struct(fmt); - if (s_object == NULL) - return NULL; - n = ((PyStructObject *)s_object)->s_size; - Py_DECREF(s_object); - return PyInt_FromSsize_t(n); -} - -PyDoc_STRVAR(pack_doc, -"Return string containing values v1, v2, ... packed according to fmt."); - -static PyObject * -pack(PyObject *self, PyObject *args) -{ - PyObject *s_object, *fmt, *newargs, *result; - Py_ssize_t n = PyTuple_GET_SIZE(args); - - 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); - if (s_object == NULL) { - Py_DECREF(newargs); - return NULL; - } - result = s_pack(s_object, newargs); - Py_DECREF(newargs); - Py_DECREF(s_object); - return result; -} - -PyDoc_STRVAR(pack_into_doc, -"Pack the values v1, v2, ... according to fmt.\n\ -Write the packed bytes into the writable buffer buf starting at offset."); - -static PyObject * -pack_into(PyObject *self, PyObject *args) -{ - PyObject *s_object, *fmt, *newargs, *result; - Py_ssize_t n = PyTuple_GET_SIZE(args); - - 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); - if (s_object == NULL) { - Py_DECREF(newargs); - return NULL; - } - result = s_pack_into(s_object, newargs); - Py_DECREF(newargs); - Py_DECREF(s_object); - return result; -} - -PyDoc_STRVAR(unpack_doc, -"Unpack the string containing packed C structure data, according to fmt.\n\ -Requires len(string) == calcsize(fmt)."); - -static PyObject * -unpack(PyObject *self, PyObject *args) -{ - PyObject *s_object, *fmt, *inputstr, *result; - - if (!PyArg_UnpackTuple(args, "unpack", 2, 2, &fmt, &inputstr)) - return NULL; - - s_object = cache_struct(fmt); - if (s_object == NULL) - return NULL; - result = s_unpack(s_object, inputstr); - Py_DECREF(s_object); - return result; -} - -PyDoc_STRVAR(unpack_from_doc, -"Unpack the buffer, containing packed C structure data, according to\n\ -fmt, starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt)."); - -static PyObject * -unpack_from(PyObject *self, PyObject *args, PyObject *kwds) -{ - PyObject *s_object, *fmt, *newargs, *result; - Py_ssize_t n = PyTuple_GET_SIZE(args); - - 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); - if (s_object == NULL) { - Py_DECREF(newargs); - return NULL; - } - result = s_unpack_from(s_object, newargs, kwds); - Py_DECREF(newargs); - Py_DECREF(s_object); - return result; -} - -static struct PyMethodDef module_functions[] = { - {"_clearcache", (PyCFunction)clearcache, METH_NOARGS, clearcache_doc}, - {"calcsize", calcsize, METH_O, calcsize_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}, - {NULL, NULL} /* sentinel */ -}; - - -/* Module initialization */ - -PyDoc_STRVAR(module_doc, -"Functions to convert between Python values and C structs represented\n\ -as Python strings. It uses format strings (explained below) as compact\n\ -descriptions of the lay-out of the C structs and the intended conversion\n\ -to/from Python values.\n\ -\n\ -The remaining chars indicate types of args and must match exactly;\n\ -these can be preceded by a decimal repeat count:\n\ - x: pad byte (no data); c:char; b:signed byte; B:unsigned byte;\n\ - ?: _Bool (requires C99; if not available, char is used instead)\n\ - h:short; H:unsigned short; i:int; I:unsigned int;\n\ - l:long; L:unsigned long; f:float; d:double.\n\ -Special cases (preceding decimal count indicates length):\n\ - s:string (array of char); p: pascal string (with count byte).\n\ -Special case (only available in native format):\n\ - P:an integer type that is wide enough to hold a pointer.\n\ -Special case (not in native mode unless 'long long' in platform C):\n\ - q:long long; Q:unsigned long long\n\ -Whitespace between formats is ignored.\n\ -\n\ -The variable struct.error is an exception raised on errors.\n"); - -PyMODINIT_FUNC -init_pvt_struct(void) -{ - import_array(); - - PyObject *ver, *m; - - ver = PyString_FromString("0.2"); - if (ver == NULL) - return; - - m = Py_InitModule3("_pvt_struct", module_functions, module_doc); - if (m == NULL) - return; - - Py_TYPE(&PyStructType) = &PyType_Type; - if (PyType_Ready(&PyStructType) < 0) - return; - - /* This speed trick can't be used until overflow masking goes - away, because native endian always raises exceptions - instead of overflow masking. */ - - /* Add some symbolic constants to the module */ - if (StructError == NULL) { - StructError = PyErr_NewException("pycuda._pvt_struct.error", NULL, NULL); - if (StructError == NULL) - return; - } - - Py_INCREF(StructError); - PyModule_AddObject(m, "error", StructError); - - Py_INCREF((PyObject*)&PyStructType); - PyModule_AddObject(m, "Struct", (PyObject*)&PyStructType); - - PyModule_AddObject(m, "__version__", ver); - - PyModule_AddIntConstant(m, "_PY_STRUCT_RANGE_CHECKING", 1); -#ifdef PY_STRUCT_FLOAT_COERCE - PyModule_AddIntConstant(m, "_PY_STRUCT_FLOAT_COERCE", 1); -#endif - -} - -// vim: noexpandtab:sw=8 diff --git a/src/wrapper/_pvt_struct_v3.cpp b/src/wrapper/_pvt_struct_v3.cpp index 15f7b18e..1fefe488 100644 --- a/src/wrapper/_pvt_struct_v3.cpp +++ b/src/wrapper/_pvt_struct_v3.cpp @@ -1,18 +1,29 @@ +// based on +// https://raw.githubusercontent.com/python/cpython/v3.8.14/Modules/_struct.c + /* struct module -- pack values into and (out of) bytes objects */ /* New version supporting byte order, alignment and size options, character strings, and unsigned numbers */ -#define PY_ARRAY_UNIQUE_SYMBOL pycuda_pvt_struct_v2_ARRAY_API + +#define PY_ARRAY_UNIQUE_SYMBOL pycuda_pvt_struct_v3_ARRAY_API #define PY_SSIZE_T_CLEAN #include "Python.h" -#include #include "structmember.h" +#include #include -namespace { +#define _Bool bool + +/*[clinic input] +class Struct "PyStructObject *" "&PyStructType" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=9b032058a83ed7c3]*/ + +namespace{ extern PyTypeObject PyStructType; } @@ -31,6 +42,7 @@ typedef struct _formatcode { const struct _formatdef *fmtdef; Py_ssize_t offset; Py_ssize_t size; + Py_ssize_t repeat; } formatcode; /* Struct object interface */ @@ -64,6 +76,7 @@ typedef struct { char c; float x; } st_float; typedef struct { char c; double x; } st_double; typedef struct { char c; void *x; } st_void_p; typedef struct { char c; size_t x; } st_size_t; +typedef struct { char c; _Bool x; } st_bool; #define SHORT_ALIGN (sizeof(st_short) - sizeof(short)) #define INT_ALIGN (sizeof(st_int) - sizeof(int)) @@ -72,29 +85,32 @@ typedef struct { char c; size_t x; } st_size_t; #define DOUBLE_ALIGN (sizeof(st_double) - sizeof(double)) #define VOID_P_ALIGN (sizeof(st_void_p) - sizeof(void *)) #define SIZE_T_ALIGN (sizeof(st_size_t) - sizeof(size_t)) +#define BOOL_ALIGN (sizeof(st_bool) - sizeof(_Bool)) /* We can't support q and Q in native mode unless the compiler does; in std mode, they're 8 bytes on all platforms. */ -#ifdef HAVE_LONG_LONG -typedef struct { char c; PY_LONG_LONG x; } s_long_long; -#define LONG_LONG_ALIGN (sizeof(s_long_long) - sizeof(PY_LONG_LONG)) -#endif - -#if !defined(__cplusplus) && defined(HAVE_C99_BOOL) -#define BOOL_TYPE _Bool -typedef struct { char c; _Bool x; } s_bool; -#define BOOL_ALIGN (sizeof(s_bool) - sizeof(BOOL_TYPE)) -#else -#define BOOL_TYPE char -#define BOOL_ALIGN 0 -#endif - -#define STRINGIFY(x) #x +typedef struct { char c; long long x; } s_long_long; +#define LONG_LONG_ALIGN (sizeof(s_long_long) - sizeof(long long)) #ifdef __powerc #pragma options align=reset #endif +/*[python input] +class cache_struct_converter(CConverter): + type = 'PyStructObject *' + converter = 'cache_struct_converter' + c_default = "NULL" + + def cleanup(self): + return "Py_XDECREF(%s);\n" % self.name +[python start generated code]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=49957cca130ffb63]*/ + +static int cache_struct_converter(PyObject *, PyStructObject **); + +#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. */ @@ -170,14 +186,12 @@ get_ulong(PyObject *v, unsigned long *p) return 0; } -#ifdef HAVE_LONG_LONG - /* Same, but handling native long long. */ static int -get_longlong(PyObject *v, PY_LONG_LONG *p) +get_longlong(PyObject *v, long long *p) { - PY_LONG_LONG x; + long long x; v = get_pylong(v); if (v == NULL) @@ -185,7 +199,7 @@ get_longlong(PyObject *v, PY_LONG_LONG *p) assert(PyLong_Check(v)); x = PyLong_AsLongLong(v); Py_DECREF(v); - if (x == (PY_LONG_LONG)-1 && PyErr_Occurred()) { + if (x == (long long)-1 && PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) PyErr_SetString(StructError, "argument out of range"); @@ -198,9 +212,9 @@ get_longlong(PyObject *v, PY_LONG_LONG *p) /* Same, but handling native unsigned long long. */ static int -get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p) +get_ulonglong(PyObject *v, unsigned long long *p) { - unsigned PY_LONG_LONG x; + unsigned long long x; v = get_pylong(v); if (v == NULL) @@ -208,7 +222,7 @@ get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p) assert(PyLong_Check(v)); x = PyLong_AsUnsignedLongLong(v); Py_DECREF(v); - if (x == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) { + if (x == (unsigned long long)-1 && PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) PyErr_SetString(StructError, "argument out of range"); @@ -218,8 +232,6 @@ get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p) return 0; } -#endif - /* Same, but handling Py_ssize_t */ static int @@ -271,7 +283,41 @@ get_size_t(PyObject *v, size_t *p) /* Floating point helpers */ -#if 0 + +static PyObject * +unpack_halffloat(const char *p, /* start of 2-byte string */ + int le) /* true for little-endian, false for big-endian */ +{ + double x; + +#if PY_VERSION_HEX >= 0x030b0000 + x = PyFloat_Unpack2(p, le); +#else + x = _PyFloat_Unpack2((unsigned char *)p, le); +#endif + if (x == -1.0 && PyErr_Occurred()) { + return NULL; + } + return PyFloat_FromDouble(x); +} + +static int +pack_halffloat(char *p, /* start of 2-byte string */ + PyObject *v, /* value to pack */ + int le) /* true for little-endian, false for big-endian */ +{ + double x = PyFloat_AsDouble(v); + if (x == -1.0 && PyErr_Occurred()) { + PyErr_SetString(StructError, + "required argument is not a float"); + return -1; + } +#if PY_VERSION_HEX >= 0x030b0000 + return PyFloat_Pack2(x, p, le); +#else + return _PyFloat_Pack2(x, (unsigned char *)p, le); +#endif +} static PyObject * unpack_float(const char *p, /* start of 4-byte string */ @@ -279,7 +325,11 @@ unpack_float(const char *p, /* start of 4-byte string */ { double x; +#if PY_VERSION_HEX >= 0x030b0000 + x = PyFloat_Unpack4(p, le); +#else x = _PyFloat_Unpack4((unsigned char *)p, le); +#endif if (x == -1.0 && PyErr_Occurred()) return NULL; return PyFloat_FromDouble(x); @@ -291,14 +341,16 @@ unpack_double(const char *p, /* start of 8-byte string */ { double x; +#if PY_VERSION_HEX >= 0x030b0000 + x = PyFloat_Unpack8(p, le); +#else x = _PyFloat_Unpack8((unsigned char *)p, le); +#endif if (x == -1.0 && PyErr_Occurred()) return NULL; return PyFloat_FromDouble(x); } -#endif - /* Helper to format the range error exceptions */ static int _range_error(const formatdef *f, int is_unsigned) @@ -341,8 +393,6 @@ _range_error(const formatdef *f, int is_unsigned) TYPE is one of char, byte, ubyte, etc. */ -// {{{ - /* Native mode routines. ****************************************************/ /* NOTE: In all n[up]_ routines handling types larger than 1 byte, there is @@ -400,13 +450,7 @@ nu_uint(const char *p, const formatdef *f) { unsigned int x; memcpy((char *)&x, p, sizeof x); -#if (SIZEOF_LONG > SIZEOF_INT) - return PyLong_FromLong((long)x); -#else - if (x <= ((unsigned int)LONG_MAX)) - return PyLong_FromLong((long)x); return PyLong_FromUnsignedLong((unsigned long)x); -#endif } static PyObject * @@ -422,8 +466,6 @@ nu_ulong(const char *p, const formatdef *f) { unsigned long x; memcpy((char *)&x, p, sizeof x); - if (x <= LONG_MAX) - return PyLong_FromLong((long)x); return PyLong_FromUnsignedLong(x); } @@ -443,43 +485,41 @@ nu_size_t(const char *p, const formatdef *f) return PyLong_FromSize_t(x); } - -/* Native mode doesn't support q or Q unless the platform C supports - long long (or, on Windows, __int64). */ - -#ifdef HAVE_LONG_LONG - static PyObject * nu_longlong(const char *p, const formatdef *f) { - PY_LONG_LONG x; + long long x; memcpy((char *)&x, p, sizeof x); - if (x >= LONG_MIN && x <= LONG_MAX) - return PyLong_FromLong(Py_SAFE_DOWNCAST(x, PY_LONG_LONG, long)); return PyLong_FromLongLong(x); } static PyObject * nu_ulonglong(const char *p, const formatdef *f) { - unsigned PY_LONG_LONG x; + unsigned long long x; memcpy((char *)&x, p, sizeof x); - if (x <= LONG_MAX) - return PyLong_FromLong(Py_SAFE_DOWNCAST(x, unsigned PY_LONG_LONG, long)); return PyLong_FromUnsignedLongLong(x); } -#endif - static PyObject * nu_bool(const char *p, const formatdef *f) { - BOOL_TYPE x; + _Bool x; memcpy((char *)&x, p, sizeof x); return PyBool_FromLong(x != 0); } +static PyObject * +nu_halffloat(const char *p, const formatdef *f) +{ +#if PY_LITTLE_ENDIAN + return unpack_halffloat(p, 1); +#else + return unpack_halffloat(p, 0); +#endif +} + static PyObject * nu_float(const char *p, const formatdef *f) { @@ -528,7 +568,7 @@ np_byte(char *p, PyObject *v, const formatdef *f) long x; if (get_long(v, &x) < 0) return -1; - if (x < -128 || x > 127){ + if (x < -128 || x > 127) { PyErr_SetString(StructError, "byte format requires -128 <= number <= 127"); return -1; @@ -543,24 +583,24 @@ np_ubyte(char *p, PyObject *v, const formatdef *f) long x; if (get_long(v, &x) < 0) return -1; - if (x < 0 || x > 255){ + if (x < 0 || x > 255) { PyErr_SetString(StructError, "ubyte format requires 0 <= number <= 255"); return -1; } - *p = (char)x; + *(unsigned char *)p = (unsigned char)x; return 0; } static int np_char(char *p, PyObject *v, const formatdef *f) { - if (!PyBytes_Check(v) || PyBytes_Size(v) != 1) { + if (!PyBytes_Check(v) || PyBytes_GET_SIZE(v) != 1) { PyErr_SetString(StructError, "char format requires a bytes object of length 1"); return -1; } - *p = *PyBytes_AsString(v); + *p = *PyBytes_AS_STRING(v); return 0; } @@ -571,10 +611,10 @@ np_short(char *p, PyObject *v, const formatdef *f) short y; if (get_long(v, &x) < 0) return -1; - if (x < SHRT_MIN || x > SHRT_MAX){ + if (x < SHRT_MIN || x > SHRT_MAX) { PyErr_SetString(StructError, - "short format requires " STRINGIFY(SHRT_MIN) - " <= number <= " STRINGIFY(SHRT_MAX)); + "short format requires " Py_STRINGIFY(SHRT_MIN) + " <= number <= " Py_STRINGIFY(SHRT_MAX)); return -1; } y = (short)x; @@ -589,9 +629,10 @@ np_ushort(char *p, PyObject *v, const formatdef *f) unsigned short y; if (get_long(v, &x) < 0) return -1; - if (x < 0 || x > USHRT_MAX){ + if (x < 0 || x > USHRT_MAX) { PyErr_SetString(StructError, - "ushort format requires 0 <= number <= " STRINGIFY(USHRT_MAX)); + "ushort format requires 0 <= number <= " + Py_STRINGIFY(USHRT_MAX)); return -1; } y = (unsigned short)x; @@ -671,12 +712,10 @@ np_size_t(char *p, PyObject *v, const formatdef *f) return 0; } -#ifdef HAVE_LONG_LONG - static int np_longlong(char *p, PyObject *v, const formatdef *f) { - PY_LONG_LONG x; + long long x; if (get_longlong(v, &x) < 0) return -1; memcpy(p, (char *)&x, sizeof x); @@ -686,20 +725,19 @@ np_longlong(char *p, PyObject *v, const formatdef *f) static int np_ulonglong(char *p, PyObject *v, const formatdef *f) { - unsigned PY_LONG_LONG x; + unsigned long long x; if (get_ulonglong(v, &x) < 0) return -1; memcpy(p, (char *)&x, sizeof x); return 0; } -#endif static int np_bool(char *p, PyObject *v, const formatdef *f) { int y; - BOOL_TYPE x; + _Bool x; y = PyObject_IsTrue(v); if (y < 0) return -1; @@ -708,6 +746,16 @@ np_bool(char *p, PyObject *v, const formatdef *f) return 0; } +static int +np_halffloat(char *p, PyObject *v, const formatdef *f) +{ +#if PY_LITTLE_ENDIAN + return pack_halffloat(p, v, 1); +#else + return pack_halffloat(p, v, 0); +#endif +} + static int np_float(char *p, PyObject *v, const formatdef *f) { @@ -793,7 +841,6 @@ np_complex_double(char *p, PyObject *v, const formatdef *f) return 0; } - static int np_void_p(char *p, PyObject *v, const formatdef *f) { @@ -811,7 +858,7 @@ np_void_p(char *p, PyObject *v, const formatdef *f) return 0; } -static formatdef native_table[] = { +static const formatdef native_table[] = { {'x', sizeof(char), 0, NULL}, {'b', sizeof(char), 0, nu_byte, np_byte}, {'B', sizeof(char), 0, nu_ubyte, np_ubyte}, @@ -826,26 +873,478 @@ static formatdef native_table[] = { {'L', sizeof(long), LONG_ALIGN, nu_ulong, np_ulong}, {'n', sizeof(size_t), SIZE_T_ALIGN, nu_ssize_t, np_ssize_t}, {'N', sizeof(size_t), SIZE_T_ALIGN, nu_size_t, np_size_t}, -#ifdef HAVE_LONG_LONG - {'q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_longlong, np_longlong}, - {'Q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_ulonglong,np_ulonglong}, -#endif - {'?', sizeof(BOOL_TYPE), BOOL_ALIGN, nu_bool, np_bool}, + {'q', sizeof(long long), LONG_LONG_ALIGN, nu_longlong, np_longlong}, + {'Q', sizeof(long long), LONG_LONG_ALIGN, nu_ulonglong,np_ulonglong}, + {'?', sizeof(_Bool), BOOL_ALIGN, nu_bool, np_bool}, + {'e', sizeof(short), SHORT_ALIGN, nu_halffloat, np_halffloat}, {'f', sizeof(float), FLOAT_ALIGN, nu_float, np_float}, {'d', sizeof(double), DOUBLE_ALIGN, nu_double, np_double}, - {'F', 2*sizeof(float), FLOAT_ALIGN, nu_complex_float, np_complex_float}, - {'D', 2*sizeof(double), DOUBLE_ALIGN, nu_complex_double, np_complex_double}, + {'F', 2*sizeof(float), FLOAT_ALIGN, nu_complex_float, np_complex_float}, + {'D', 2*sizeof(double), DOUBLE_ALIGN, nu_complex_double, np_complex_double}, {'P', sizeof(void *), VOID_P_ALIGN, nu_void_p, np_void_p}, {0} }; -// }}} +/* Big-endian routines. *****************************************************/ + +static PyObject * +bu_int(const char *p, const formatdef *f) +{ + long x = 0; + Py_ssize_t i = f->size; + const unsigned char *bytes = (const unsigned char *)p; + do { + x = (x<<8) | *bytes++; + } while (--i > 0); + /* Extend the sign bit. */ + if (SIZEOF_LONG > f->size) + x |= -(x & (1L << ((8 * f->size) - 1))); + return PyLong_FromLong(x); +} + +static PyObject * +bu_uint(const char *p, const formatdef *f) +{ + unsigned long x = 0; + Py_ssize_t i = f->size; + const unsigned char *bytes = (const unsigned char *)p; + do { + x = (x<<8) | *bytes++; + } while (--i > 0); + return PyLong_FromUnsignedLong(x); +} + +static PyObject * +bu_longlong(const char *p, const formatdef *f) +{ + long long x = 0; + Py_ssize_t i = f->size; + const unsigned char *bytes = (const unsigned char *)p; + do { + x = (x<<8) | *bytes++; + } while (--i > 0); + /* Extend the sign bit. */ + if (SIZEOF_LONG_LONG > f->size) + x |= -(x & ((long long)1 << ((8 * f->size) - 1))); + return PyLong_FromLongLong(x); +} + +static PyObject * +bu_ulonglong(const char *p, const formatdef *f) +{ + unsigned long long x = 0; + Py_ssize_t i = f->size; + const unsigned char *bytes = (const unsigned char *)p; + do { + x = (x<<8) | *bytes++; + } while (--i > 0); + return PyLong_FromUnsignedLongLong(x); +} + +static PyObject * +bu_halffloat(const char *p, const formatdef *f) +{ + return unpack_halffloat(p, 0); +} + +static PyObject * +bu_float(const char *p, const formatdef *f) +{ + return unpack_float(p, 0); +} + +static PyObject * +bu_double(const char *p, const formatdef *f) +{ + return unpack_double(p, 0); +} + +static PyObject * +bu_bool(const char *p, const formatdef *f) +{ + return PyBool_FromLong(*p != 0); +} + +static int +bp_int(char *p, PyObject *v, const formatdef *f) +{ + long x; + Py_ssize_t i; + unsigned char *q = (unsigned char *)p; + if (get_long(v, &x) < 0) + return -1; + i = f->size; + if (i != SIZEOF_LONG) { + if ((i == 2) && (x < -32768 || x > 32767)) + RANGE_ERROR(x, f, 0, 0xffffL); +#if (SIZEOF_LONG != 4) + else if ((i == 4) && (x < -2147483648L || x > 2147483647L)) + RANGE_ERROR(x, f, 0, 0xffffffffL); +#endif + } + do { + q[--i] = (unsigned char)(x & 0xffL); + x >>= 8; + } while (i > 0); + return 0; +} + +static int +bp_uint(char *p, PyObject *v, const formatdef *f) +{ + unsigned long x; + Py_ssize_t i; + unsigned char *q = (unsigned char *)p; + if (get_ulong(v, &x) < 0) + return -1; + i = f->size; + if (i != SIZEOF_LONG) { + unsigned long maxint = 1; + maxint <<= (unsigned long)(i * 8); + if (x >= maxint) + RANGE_ERROR(x, f, 1, maxint - 1); + } + do { + q[--i] = (unsigned char)(x & 0xffUL); + x >>= 8; + } while (i > 0); + return 0; +} + +static int +bp_longlong(char *p, PyObject *v, const formatdef *f) +{ + int res; + v = get_pylong(v); + if (v == NULL) + return -1; + res = _PyLong_AsByteArray((PyLongObject *)v, + (unsigned char *)p, + 8, + 0, /* little_endian */ + 1 /* signed */); + Py_DECREF(v); + return res; +} + +static int +bp_ulonglong(char *p, PyObject *v, const formatdef *f) +{ + int res; + v = get_pylong(v); + if (v == NULL) + return -1; + res = _PyLong_AsByteArray((PyLongObject *)v, + (unsigned char *)p, + 8, + 0, /* little_endian */ + 0 /* signed */); + Py_DECREF(v); + return res; +} + +static int +bp_halffloat(char *p, PyObject *v, const formatdef *f) +{ + return pack_halffloat(p, v, 0); +} + +static int +bp_float(char *p, PyObject *v, const formatdef *f) +{ + double x = PyFloat_AsDouble(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_SetString(StructError, + "required argument is not a float"); + return -1; + } +#if PY_VERSION_HEX >= 0x030b0000 + return PyFloat_Pack4(x, p, 0); +#else + return _PyFloat_Pack4(x, (unsigned char *)p, 0); +#endif +} + +static int +bp_double(char *p, PyObject *v, const formatdef *f) +{ + double x = PyFloat_AsDouble(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_SetString(StructError, + "required argument is not a float"); + return -1; + } +#if PY_VERSION_HEX >= 0x030b0000 + return PyFloat_Pack8(x, p, 0); +#else + return _PyFloat_Pack8(x, (unsigned char *)p, 0); +#endif +} + +static int +bp_bool(char *p, PyObject *v, const formatdef *f) +{ + int y; + y = PyObject_IsTrue(v); + if (y < 0) + return -1; + *p = (char)y; + return 0; +} + +static formatdef bigendian_table[] = { + {'x', 1, 0, NULL}, + {'b', 1, 0, nu_byte, np_byte}, + {'B', 1, 0, nu_ubyte, np_ubyte}, + {'c', 1, 0, nu_char, np_char}, + {'s', 1, 0, NULL}, + {'p', 1, 0, NULL}, + {'h', 2, 0, bu_int, bp_int}, + {'H', 2, 0, bu_uint, bp_uint}, + {'i', 4, 0, bu_int, bp_int}, + {'I', 4, 0, bu_uint, bp_uint}, + {'l', 4, 0, bu_int, bp_int}, + {'L', 4, 0, bu_uint, bp_uint}, + {'q', 8, 0, bu_longlong, bp_longlong}, + {'Q', 8, 0, bu_ulonglong, bp_ulonglong}, + {'?', 1, 0, bu_bool, bp_bool}, + {'e', 2, 0, bu_halffloat, bp_halffloat}, + {'f', 4, 0, bu_float, bp_float}, + {'d', 8, 0, bu_double, bp_double}, + {0} +}; + +/* Little-endian routines. *****************************************************/ + +static PyObject * +lu_int(const char *p, const formatdef *f) +{ + long x = 0; + Py_ssize_t i = f->size; + const unsigned char *bytes = (const unsigned char *)p; + do { + x = (x<<8) | bytes[--i]; + } while (i > 0); + /* Extend the sign bit. */ + if (SIZEOF_LONG > f->size) + x |= -(x & (1L << ((8 * f->size) - 1))); + return PyLong_FromLong(x); +} + +static PyObject * +lu_uint(const char *p, const formatdef *f) +{ + unsigned long x = 0; + Py_ssize_t i = f->size; + const unsigned char *bytes = (const unsigned char *)p; + do { + x = (x<<8) | bytes[--i]; + } while (i > 0); + return PyLong_FromUnsignedLong(x); +} + +static PyObject * +lu_longlong(const char *p, const formatdef *f) +{ + long long x = 0; + Py_ssize_t i = f->size; + const unsigned char *bytes = (const unsigned char *)p; + do { + x = (x<<8) | bytes[--i]; + } while (i > 0); + /* Extend the sign bit. */ + if (SIZEOF_LONG_LONG > f->size) + x |= -(x & ((long long)1 << ((8 * f->size) - 1))); + return PyLong_FromLongLong(x); +} + +static PyObject * +lu_ulonglong(const char *p, const formatdef *f) +{ + unsigned long long x = 0; + Py_ssize_t i = f->size; + const unsigned char *bytes = (const unsigned char *)p; + do { + x = (x<<8) | bytes[--i]; + } while (i > 0); + return PyLong_FromUnsignedLongLong(x); +} + +static PyObject * +lu_halffloat(const char *p, const formatdef *f) +{ + return unpack_halffloat(p, 1); +} + +static PyObject * +lu_float(const char *p, const formatdef *f) +{ + return unpack_float(p, 1); +} + +static PyObject * +lu_double(const char *p, const formatdef *f) +{ + return unpack_double(p, 1); +} + +static int +lp_int(char *p, PyObject *v, const formatdef *f) +{ + long x; + Py_ssize_t i; + unsigned char *q = (unsigned char *)p; + if (get_long(v, &x) < 0) + return -1; + i = f->size; + if (i != SIZEOF_LONG) { + if ((i == 2) && (x < -32768 || x > 32767)) + RANGE_ERROR(x, f, 0, 0xffffL); +#if (SIZEOF_LONG != 4) + else if ((i == 4) && (x < -2147483648L || x > 2147483647L)) + RANGE_ERROR(x, f, 0, 0xffffffffL); +#endif + } + do { + *q++ = (unsigned char)(x & 0xffL); + x >>= 8; + } while (--i > 0); + return 0; +} + +static int +lp_uint(char *p, PyObject *v, const formatdef *f) +{ + unsigned long x; + Py_ssize_t i; + unsigned char *q = (unsigned char *)p; + if (get_ulong(v, &x) < 0) + return -1; + i = f->size; + if (i != SIZEOF_LONG) { + unsigned long maxint = 1; + maxint <<= (unsigned long)(i * 8); + if (x >= maxint) + RANGE_ERROR(x, f, 1, maxint - 1); + } + do { + *q++ = (unsigned char)(x & 0xffUL); + x >>= 8; + } while (--i > 0); + return 0; +} + +static int +lp_longlong(char *p, PyObject *v, const formatdef *f) +{ + int res; + v = get_pylong(v); + if (v == NULL) + return -1; + res = _PyLong_AsByteArray((PyLongObject*)v, + (unsigned char *)p, + 8, + 1, /* little_endian */ + 1 /* signed */); + Py_DECREF(v); + return res; +} + +static int +lp_ulonglong(char *p, PyObject *v, const formatdef *f) +{ + int res; + v = get_pylong(v); + if (v == NULL) + return -1; + res = _PyLong_AsByteArray((PyLongObject*)v, + (unsigned char *)p, + 8, + 1, /* little_endian */ + 0 /* signed */); + Py_DECREF(v); + return res; +} + +static int +lp_halffloat(char *p, PyObject *v, const formatdef *f) +{ + return pack_halffloat(p, v, 1); +} + +static int +lp_float(char *p, PyObject *v, const formatdef *f) +{ + double x = PyFloat_AsDouble(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_SetString(StructError, + "required argument is not a float"); + return -1; + } +#if PY_VERSION_HEX >= 0x030b0000 + return PyFloat_Pack4(x, p, 1); +#else + return _PyFloat_Pack4(x, (unsigned char *)p, 1); +#endif +} + +static int +lp_double(char *p, PyObject *v, const formatdef *f) +{ + double x = PyFloat_AsDouble(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_SetString(StructError, + "required argument is not a float"); + return -1; + } +#if PY_VERSION_HEX >= 0x030b0000 + return PyFloat_Pack8(x, p, 1); +#else + return _PyFloat_Pack8(x, (unsigned char *)p, 1); +#endif +} + +static formatdef lilendian_table[] = { + {'x', 1, 0, NULL}, + {'b', 1, 0, nu_byte, np_byte}, + {'B', 1, 0, nu_ubyte, np_ubyte}, + {'c', 1, 0, nu_char, np_char}, + {'s', 1, 0, NULL}, + {'p', 1, 0, NULL}, + {'h', 2, 0, lu_int, lp_int}, + {'H', 2, 0, lu_uint, lp_uint}, + {'i', 4, 0, lu_int, lp_int}, + {'I', 4, 0, lu_uint, lp_uint}, + {'l', 4, 0, lu_int, lp_int}, + {'L', 4, 0, lu_uint, lp_uint}, + {'q', 8, 0, lu_longlong, lp_longlong}, + {'Q', 8, 0, lu_ulonglong, lp_ulonglong}, + {'?', 1, 0, bu_bool, bp_bool}, /* Std rep not endian dep, + but potentially different from native rep -- reuse bx_bool funcs. */ + {'e', 2, 0, lu_halffloat, lp_halffloat}, + {'f', 4, 0, lu_float, lp_float}, + {'d', 8, 0, lu_double, lp_double}, + {0} +}; + static const formatdef * -whichtable(char **pfmt) +whichtable(const char **pfmt) { const char *fmt = (*pfmt)++; /* May be backed out of later */ switch (*fmt) { + case '<': + return lilendian_table; + case '>': + case '!': /* Network byte order is big-endian */ + return bigendian_table; + case '=': { /* Host byte order -- different from native in alignment! */ +#if PY_LITTLE_ENDIAN + return lilendian_table; +#else + return bigendian_table; +#endif + } default: --*pfmt; /* Back out of pointer increment */ /* Fall through */ @@ -888,6 +1387,9 @@ align(Py_ssize_t size, char c, const formatdef *e) return size; } +/* + * Struct object implementation. + */ /* calculate the size of a format string */ @@ -902,16 +1404,22 @@ prepare_s(PyStructObject *self) const char *fmt; char c; Py_ssize_t size, len, num, itemsize; + size_t ncodes; fmt = PyBytes_AS_STRING(self->s_format); + if (strlen(fmt) != (size_t)PyBytes_GET_SIZE(self->s_format)) { + PyErr_SetString(StructError, "embedded null character"); + return -1; + } - f = whichtable((char **)&fmt); + f = whichtable(&fmt); s = fmt; size = 0; len = 0; + ncodes = 0; while ((c = *s++) != '\0') { - if (isspace(Py_CHARMASK(c))) + if (Py_ISSPACE(Py_CHARMASK(c))) continue; if ('0' <= c && c <= '9') { num = c - '0'; @@ -939,9 +1447,9 @@ prepare_s(PyStructObject *self) switch (c) { case 's': /* fall through */ - case 'p': len++; break; + case 'p': len++; ncodes++; break; case 'x': break; - default: len += num; break; + default: len += num; if (num) ncodes++; break; } itemsize = e->size; @@ -956,14 +1464,14 @@ prepare_s(PyStructObject *self) } /* check for overflow */ - if ((len + 1) > (PY_SSIZE_T_MAX / sizeof(formatcode))) { + if ((ncodes + 1) > ((size_t)PY_SSIZE_T_MAX / sizeof(formatcode))) { PyErr_NoMemory(); return -1; } self->s_size = size; self->s_len = len; - codes = (formatcode *) PyMem_MALLOC((len + 1) * sizeof(formatcode)); + codes = (formatcode *) PyMem_MALLOC((ncodes + 1) * sizeof(formatcode)); if (codes == NULL) { PyErr_NoMemory(); return -1; @@ -976,14 +1484,12 @@ prepare_s(PyStructObject *self) s = fmt; size = 0; while ((c = *s++) != '\0') { - if (isspace(Py_CHARMASK(c))) + if (Py_ISSPACE(Py_CHARMASK(c))) continue; if ('0' <= c && c <= '9') { num = c - '0'; while ('0' <= (c = *s++) && c <= '9') num = num*10 + (c - '0'); - if (c == '\0') - break; } else num = 1; @@ -995,23 +1501,24 @@ prepare_s(PyStructObject *self) codes->offset = size; codes->size = num; codes->fmtdef = e; + codes->repeat = 1; codes++; size += num; } else if (c == 'x') { size += num; - } else { - while (--num >= 0) { - codes->offset = size; - codes->size = e->size; - codes->fmtdef = e; - codes++; - size += e->size; - } + } else if (num) { + codes->offset = size; + codes->size = e->size; + codes->fmtdef = e; + codes->repeat = num; + codes++; + size += e->size * num; } } codes->fmtdef = NULL; codes->offset = size; codes->size = 0; + codes->repeat = 0; return 0; @@ -1040,42 +1547,47 @@ s_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 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); + "Struct() argument 1 must be a str or bytes object, " + "not %.200s", + Py_TYPE(format)->tp_name); return -1; } - Py_CLEAR(soself->s_format); - soself->s_format = o_format; + Py_SETREF(self->s_format, format); - ret = prepare_s(soself); + ret = prepare_s(self); return ret; } @@ -1087,12 +1599,12 @@ s_dealloc(PyStructObject *s) if (s->s_codes != NULL) { PyMem_FREE(s->s_codes); } - Py_XDECREF(s->s_format); + Py_DECREF(s->s_format); Py_TYPE(s)->tp_free((PyObject *)s); } static PyObject * -s_unpack_internal(PyStructObject *soself, char *startfrom) { +s_unpack_internal(PyStructObject *soself, const char *startfrom) { formatcode *code; Py_ssize_t i = 0; PyObject *result = PyTuple_New(soself->s_len); @@ -1100,22 +1612,26 @@ s_unpack_internal(PyStructObject *soself, char *startfrom) { return NULL; for (code = soself->s_codes; code->fmtdef != NULL; code++) { - PyObject *v; const formatdef *e = code->fmtdef; const char *res = startfrom + code->offset; - if (e->format == 's') { - v = PyBytes_FromStringAndSize(res, code->size); - } else if (e->format == 'p') { - Py_ssize_t n = *(unsigned char*)res; - if (n >= code->size) - n = code->size - 1; - v = PyBytes_FromStringAndSize(res + 1, n); - } else { - v = e->unpack(res, e); + Py_ssize_t j = code->repeat; + while (j--) { + PyObject *v; + if (e->format == 's') { + v = PyBytes_FromStringAndSize(res, code->size); + } else if (e->format == 'p') { + Py_ssize_t n = *(unsigned char*)res; + if (n >= code->size) + n = code->size - 1; + v = PyBytes_FromStringAndSize(res + 1, n); + } else { + v = e->unpack(res, e); + } + if (v == NULL) + goto fail; + PyTuple_SET_ITEM(result, i++, v); + res += code->size; } - if (v == NULL) - goto fail; - PyTuple_SET_ITEM(result, i++, v); } return result; @@ -1125,75 +1641,233 @@ s_unpack_internal(PyStructObject *soself, char *startfrom) { } -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. Requires len(buffer) == S.size. See help(struct)\n\ -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) + assert(self->s_codes != NULL); + if (buffer->len != self->s_size) { + PyErr_Format(StructError, + "unpack requires a buffer of %zd bytes", + self->s_size); return NULL; - if (vbuf.len != soself->s_size) { + } + return s_unpack_internal(self, (const char *)buffer->buf); +} + +/*[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, starting at position offset, must be +at least Struct.size. + +See help(struct) for more on format strings. +[clinic start generated code]*/ + +static PyObject * +Struct_unpack_from_impl(PyStructObject *self, Py_buffer *buffer, + Py_ssize_t offset) +/*[clinic end generated code: output=57fac875e0977316 input=cafd4851d473c894]*/ +{ + assert(self->s_codes != NULL); + + if (offset < 0) { + if (offset + self->s_size > 0) { + PyErr_Format(StructError, + "not enough data to unpack %zd bytes at offset %zd", + self->s_size, + offset); + return NULL; + } + + if (offset + buffer->len < 0) { + PyErr_Format(StructError, + "offset %zd out of range for %zd-byte buffer", + offset, + buffer->len); + return NULL; + } + offset += buffer->len; + } + + if ((buffer->len - offset) < self->s_size) { PyErr_Format(StructError, - "unpack requires a bytes object of length %zd", - soself->s_size); - PyBuffer_Release(&vbuf); + "unpack_from requires a buffer of at least %zu bytes for " + "unpacking %zd bytes at offset %zd " + "(actual buffer size is %zd)", + (size_t)self->s_size + (size_t)offset, + self->s_size, + offset, + buffer->len); return NULL; } - result = s_unpack_internal(soself, (char *) vbuf.buf); - PyBuffer_Release(&vbuf); - return result; + return s_unpack_internal(self, (char*)buffer->buf + offset); } -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. Requires len(buffer[offset:]) >= S.size. See\n\ -help(struct) for more on format strings."); + + +/* Unpack iterator type */ + +typedef struct { + PyObject_HEAD + PyStructObject *so; + Py_buffer buf; + Py_ssize_t index; +} unpackiterobject; + +static void +unpackiter_dealloc(unpackiterobject *self) +{ + /* bpo-31095: UnTrack is needed before calling any callbacks */ + PyObject_GC_UnTrack(self); + Py_XDECREF(self->so); + PyBuffer_Release(&self->buf); + PyObject_GC_Del(self); +} + +static int +unpackiter_traverse(unpackiterobject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->so); + Py_VISIT(self->buf.obj); + return 0; +} static PyObject * -s_unpack_from(PyObject *self, PyObject *args, PyObject *kwds) +unpackiter_len(unpackiterobject *self, PyObject *Py_UNUSED(ignored)) { - static char *kwlist[] = {"buffer", "offset", 0}; + Py_ssize_t len; + if (self->so == NULL) + len = 0; + else + len = (self->buf.len - self->index) / self->so->s_size; + return PyLong_FromSsize_t(len); +} + +static PyMethodDef unpackiter_methods[] = { + {"__length_hint__", (PyCFunction) unpackiter_len, METH_NOARGS, NULL}, + {NULL, NULL} /* sentinel */ +}; - PyObject *input; - Py_ssize_t offset = 0; - Py_buffer vbuf; +static PyObject * +unpackiter_iternext(unpackiterobject *self) +{ PyObject *result; - PyStructObject *soself = (PyStructObject *)self; + if (self->so == NULL) + return NULL; + if (self->index >= self->buf.len) { + /* Iterator exhausted */ + Py_CLEAR(self->so); + PyBuffer_Release(&self->buf); + return NULL; + } + assert(self->index + self->so->s_size <= self->buf.len); + result = s_unpack_internal(self->so, + (char*) self->buf.buf + self->index); + self->index += self->so->s_size; + return result; +} - assert(PyStruct_Check(self)); - assert(soself->s_codes != NULL); +static PyTypeObject unpackiter_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "unpack_iterator", /* tp_name */ + sizeof(unpackiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)unpackiter_dealloc, /* tp_dealloc */ + 0, /* tp_vectorcall_offset */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_as_async */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)unpackiter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)unpackiter_iternext, /* tp_iternext */ + unpackiter_methods /* tp_methods */ +}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|n:unpack_from", kwlist, - &input, &offset)) +/*[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 * +Struct_iter_unpack(PyStructObject *self, PyObject *buffer) +/*[clinic end generated code: output=172d83d0cd15dbab input=6d65b3f3107dbc99]*/ +{ + unpackiterobject *iter; + + assert(self->s_codes != NULL); + + if (self->s_size == 0) { + PyErr_Format(StructError, + "cannot iteratively unpack with a struct of length 0"); + return NULL; + } + + iter = (unpackiterobject *) PyType_GenericAlloc(&unpackiter_type, 0); + if (iter == NULL) return NULL; - if (PyObject_GetBuffer(input, &vbuf, PyBUF_SIMPLE) < 0) + + if (PyObject_GetBuffer(buffer, &iter->buf, PyBUF_SIMPLE) < 0) { + Py_DECREF(iter); return NULL; - if (offset < 0) - offset += vbuf.len; - if (offset < 0 || vbuf.len - offset < soself->s_size) { + } + if (iter->buf.len % self->s_size != 0) { PyErr_Format(StructError, - "unpack_from requires a buffer of at least %zd bytes", - soself->s_size); - PyBuffer_Release(&vbuf); + "iterative unpacking requires a buffer of " + "a multiple of %zd bytes", + self->s_size); + Py_DECREF(iter); return NULL; } - result = s_unpack_internal(soself, (char*)vbuf.buf + offset); - PyBuffer_Release(&vbuf); - return result; + Py_INCREF(self); + iter->so = self; + iter->index = 0; + return (PyObject *)iter; } @@ -1208,7 +1882,7 @@ s_unpack_from(PyObject *self, PyObject *args, PyObject *kwds) * */ static int -s_pack_internal(PyStructObject *soself, PyObject *args, int offset, char* buf) +s_pack_internal(PyStructObject *soself, PyObject *const *args, int offset, char* buf) { formatcode *code; /* XXX(nnorwitz): why does i need to be a local? can we use @@ -1218,79 +1892,67 @@ s_pack_internal(PyStructObject *soself, PyObject *args, int offset, char* buf) memset(buf, '\0', soself->s_size); i = offset; for (code = soself->s_codes; code->fmtdef != NULL; code++) { - Py_ssize_t n; - PyObject *v = PyTuple_GET_ITEM(args, i++); const formatdef *e = code->fmtdef; char *res = buf + code->offset; - if (e->format == 's') { - int isstring; - void *p; - if (PyBytes_Check(v)) { - n = PyBytes_GET_SIZE(v); - p = PyBytes_AS_STRING(v); - - if (n > code->size) - n = code->size; - if (n > 0) - memcpy(res, p, n); - } else if (PyByteArray_Check(v)) { - n = PyByteArray_GET_SIZE(v); - p = PyByteArray_AS_STRING(v); - + Py_ssize_t j = code->repeat; + while (j--) { + PyObject *v = args[i++]; + if (e->format == 's') { + Py_ssize_t n; + int isstring; + void *p; + isstring = PyBytes_Check(v); + if (!isstring && !PyByteArray_Check(v)) { + PyErr_SetString(StructError, + "argument for 's' must be a bytes object"); + return -1; + } + if (isstring) { + n = PyBytes_GET_SIZE(v); + p = PyBytes_AS_STRING(v); + } + else { + n = PyByteArray_GET_SIZE(v); + p = PyByteArray_AS_STRING(v); + } if (n > code->size) n = code->size; if (n > 0) memcpy(res, p, n); - } else if (PyObject_CheckBuffer(v)) { - Py_buffer view; - int gb_result = PyObject_GetBuffer(v, &view, PyBUF_SIMPLE); - - if (gb_result == -1) - return gb_result; - - n = view.len; - if (n > code->size) - n = code->size; + } else if (e->format == 'p') { + Py_ssize_t n; + int isstring; + void *p; + isstring = PyBytes_Check(v); + if (!isstring && !PyByteArray_Check(v)) { + PyErr_SetString(StructError, + "argument for 'p' must be a bytes object"); + return -1; + } + if (isstring) { + n = PyBytes_GET_SIZE(v); + p = PyBytes_AS_STRING(v); + } + else { + n = PyByteArray_GET_SIZE(v); + p = PyByteArray_AS_STRING(v); + } + if (n > (code->size - 1)) + n = code->size - 1; if (n > 0) - memcpy(res, view.buf, n); - - PyBuffer_Release(&view); + memcpy(res + 1, p, n); + if (n > 255) + n = 255; + *res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char); } else { - PyErr_SetString(StructError, - "argument for 's' must be a bytes object"); - return -1; - } - } else if (e->format == 'p') { - int isstring; - void *p; - isstring = PyBytes_Check(v); - if (!isstring && !PyByteArray_Check(v)) { - PyErr_SetString(StructError, - "argument for 'p' must be a bytes object"); - return -1; - } - if (isstring) { - n = PyBytes_GET_SIZE(v); - p = PyBytes_AS_STRING(v); - } - else { - n = PyByteArray_GET_SIZE(v); - p = PyByteArray_AS_STRING(v); - } - if (n > (code->size - 1)) - n = code->size - 1; - if (n > 0) - memcpy(res + 1, p, n); - if (n > 255) - n = 255; - *res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char); - } else { - if (e->pack(res, v, e) < 0) { - if (PyLong_Check(v) && PyErr_ExceptionMatches(PyExc_OverflowError)) - PyErr_SetString(StructError, - "long too large to convert to int"); - return -1; + if (e->pack(res, v, e) < 0) { + if (PyLong_Check(v) && PyErr_ExceptionMatches(PyExc_OverflowError)) + PyErr_SetString(StructError, + "int too large to convert"); + return -1; + } } + res += code->size; } } @@ -1307,7 +1969,7 @@ to the format string S.format. See help(struct) for more on format\n\ strings."); static PyObject * -s_pack(PyObject *self, PyObject *args) +s_pack(PyObject *self, PyObject *const *args, Py_ssize_t nargs) { PyStructObject *soself; PyObject *result; @@ -1316,14 +1978,14 @@ s_pack(PyObject *self, PyObject *args) 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 requires exactly %zd arguments", soself->s_len); + "pack expected %zd items for packing (got %zd)", soself->s_len, nargs); return NULL; } - /* Allocate a new string */ + /* Allocate a new buffer */ result = PyBytes_FromStringAndSize((char *)NULL, soself->s_size); if (result == NULL) return NULL; @@ -1346,61 +2008,103 @@ offset. Note that the offset is a required argument. See\n\ help(struct) for more on format strings."); static PyObject * -s_pack_into(PyObject *self, PyObject *args) +s_pack_into(PyObject *self, PyObject *const *args, Py_ssize_t nargs) { PyStructObject *soself; - char *buffer; - Py_ssize_t buffer_len, offset; + Py_buffer buffer; + Py_ssize_t offset; /* Validate arguments. +1 is for the first arg as buffer. */ 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)) { - PyErr_Format(StructError, - "pack_into requires exactly %zd arguments", - (soself->s_len + 2)); + if (nargs == 0) { + PyErr_Format(StructError, + "pack_into expected buffer argument"); + } + 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, (nargs - 2)); + } return NULL; } /* Extract a writable memory buffer from the first argument */ - if ( PyObject_AsWriteBuffer(PyTuple_GET_ITEM(args, 0), - (void**)&buffer, &buffer_len) == -1 ) { + if (!PyArg_Parse(args[0], "w*", &buffer)) return NULL; - } - assert( buffer_len >= 0 ); + assert(buffer.len >= 0); /* Extract the offset from the first argument */ - offset = PyNumber_AsSsize_t(PyTuple_GET_ITEM(args, 1), PyExc_IndexError); - if (offset == -1 && PyErr_Occurred()) + offset = PyNumber_AsSsize_t(args[1], PyExc_IndexError); + if (offset == -1 && PyErr_Occurred()) { + PyBuffer_Release(&buffer); return NULL; + } /* Support negative offsets. */ - if (offset < 0) - offset += buffer_len; + if (offset < 0) { + /* Check that negative offset is low enough to fit data */ + if (offset + soself->s_size > 0) { + PyErr_Format(StructError, + "no space to pack %zd bytes at offset %zd", + soself->s_size, + offset); + PyBuffer_Release(&buffer); + return NULL; + } + + /* Check that negative offset is not crossing buffer boundary */ + if (offset + buffer.len < 0) { + PyErr_Format(StructError, + "offset %zd out of range for %zd-byte buffer", + offset, + buffer.len); + PyBuffer_Release(&buffer); + return NULL; + } + + offset += buffer.len; + } /* Check boundaries */ - if (offset < 0 || (buffer_len - offset) < soself->s_size) { + if ((buffer.len - offset) < soself->s_size) { + assert(offset >= 0); + assert(soself->s_size >= 0); + PyErr_Format(StructError, - "pack_into requires a buffer of at least %zd bytes", - soself->s_size); + "pack_into requires a buffer of at least %zu bytes for " + "packing %zd bytes at offset %zd " + "(actual buffer size is %zd)", + (size_t)soself->s_size + (size_t)offset, + soself->s_size, + offset, + buffer.len); + PyBuffer_Release(&buffer); return NULL; } /* Call the guts */ - if ( s_pack_internal(soself, args, 2, buffer + offset) != 0 ) { + if (s_pack_internal(soself, args, 2, (char*)buffer.buf + offset) != 0) { + PyBuffer_Release(&buffer); return NULL; } + PyBuffer_Release(&buffer); Py_RETURN_NONE; } static PyObject * s_get_format(PyStructObject *self, void *unused) { - Py_INCREF(self->s_format); - return self->s_format; + return PyUnicode_FromStringAndSize(PyBytes_AS_STRING(self->s_format), + PyBytes_GET_SIZE(self->s_format)); } static PyObject * @@ -1409,23 +2113,33 @@ s_get_size(PyStructObject *self, void *unused) return PyLong_FromSsize_t(self->s_size); } +PyDoc_STRVAR(s_sizeof__doc__, +"S.__sizeof__() -> size of S in memory, in bytes"); + +static PyObject * +s_sizeof(PyStructObject *self, void *unused) +{ + Py_ssize_t size; + formatcode *code; + + size = _PyObject_SIZE(Py_TYPE(self)) + sizeof(formatcode); + for (code = self->s_codes; code->fmtdef != NULL; code++) + size += sizeof(formatcode); + return PyLong_FromSsize_t(size); +} + /* List of functions */ static struct PyMethodDef s_methods[] = { - {"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)(void(*)(void))s_pack, METH_FASTCALL, s_pack__doc__}, + {"pack_into", (PyCFunction)(void(*)(void))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[] = { @@ -1441,10 +2155,10 @@ PyTypeObject PyStructType = { sizeof(PyStructObject), 0, (destructor)s_dealloc, /* tp_dealloc */ - 0, /* tp_print */ + 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_reserved */ + 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -1452,29 +2166,29 @@ PyTypeObject PyStructType = { 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 */ }; } @@ -1484,191 +2198,206 @@ PyTypeObject PyStructType = { #define MAXCACHE 100 static PyObject *cache = NULL; -static PyObject * -cache_struct(PyObject *fmt) +static int +cache_struct_converter(PyObject *fmt, PyStructObject **ptr) { PyObject * s_object; + if (fmt == NULL) { + Py_DECREF(*ptr); + *ptr = NULL; + return 1; + } + if (cache == NULL) { cache = PyDict_New(); if (cache == NULL) - return NULL; + return 0; } - s_object = PyDict_GetItem(cache, fmt); + s_object = PyDict_GetItemWithError(cache, fmt); if (s_object != NULL) { Py_INCREF(s_object); - return s_object; + *ptr = (PyStructObject *)s_object; + return Py_CLEANUP_SUPPORTED; + } + else if (PyErr_Occurred()) { + return 0; } s_object = PyObject_CallFunctionObjArgs((PyObject *)(&PyStructType), fmt, NULL); if (s_object != NULL) { - if (PyDict_Size(cache) >= MAXCACHE) + if (PyDict_GET_SIZE(cache) >= MAXCACHE) PyDict_Clear(cache); /* Attempt to cache the result */ if (PyDict_SetItem(cache, fmt, s_object) == -1) PyErr_Clear(); + *ptr = (PyStructObject *)s_object; + return Py_CLEANUP_SUPPORTED; } - return s_object; + return 0; } -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."); -static PyObject * -calcsize(PyObject *self, PyObject *fmt) +/*[clinic input] +calcsize -> Py_ssize_t + + format as s_object: cache_struct + / + +Return size in bytes of the struct described by the format string. +[clinic start generated code]*/ + +static Py_ssize_t +calcsize_impl(PyObject *module, PyStructObject *s_object) +/*[clinic end generated code: output=db7d23d09c6932c4 input=96a6a590c7717ecd]*/ { - Py_ssize_t n; - PyObject *s_object = cache_struct(fmt); - if (s_object == NULL) - return NULL; - n = ((PyStructObject *)s_object)->s_size; - Py_DECREF(s_object); - return PyLong_FromSsize_t(n); + return s_object->s_size; } 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 *const *args, Py_ssize_t nargs) { - PyObject *s_object, *fmt, *newargs, *result; - Py_ssize_t n = PyTuple_GET_SIZE(args); + PyObject *s_object = NULL; + 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); - if (s_object == NULL) { - Py_DECREF(newargs); + if (!cache_struct_converter(format, (PyStructObject **)&s_object)) { return NULL; } - result = s_pack(s_object, newargs); - Py_DECREF(newargs); + result = s_pack(s_object, args + 1, nargs - 1); 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 *const *args, Py_ssize_t nargs) { - PyObject *s_object, *fmt, *newargs, *result; - Py_ssize_t n = PyTuple_GET_SIZE(args); + PyObject *s_object = NULL; + 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); - if (s_object == NULL) { - Py_DECREF(newargs); + if (!cache_struct_converter(format, (PyStructObject **)&s_object)) { return NULL; } - result = s_pack_into(s_object, newargs); - Py_DECREF(newargs); + result = s_pack_into(s_object, args + 1, nargs - 1); 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. Requires len(buffer) == calcsize(fmt). See help(struct) for more\n\ -on format strings."); +/*[clinic input] +unpack + + format as s_object: cache_struct + buffer: Py_buffer + / + +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, PyStructObject *s_object, Py_buffer *buffer) +/*[clinic end generated code: output=48ddd4d88eca8551 input=05fa3b91678da727]*/ { - PyObject *s_object, *fmt, *inputstr, *result; + return Struct_unpack_impl(s_object, buffer); +} - if (!PyArg_UnpackTuple(args, "unpack", 2, 2, &fmt, &inputstr)) - return NULL; +/*[clinic input] +unpack_from - s_object = cache_struct(fmt); - if (s_object == NULL) - return NULL; - result = s_unpack(s_object, inputstr); - Py_DECREF(s_object); - return result; -} + format as s_object: cache_struct + / + buffer: Py_buffer + offset: Py_ssize_t = 0 -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. Requires len(buffer[offset:]) >= calcsize(fmt). See help(struct)\n\ -for more on format strings."); +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, PyStructObject *s_object, + Py_buffer *buffer, Py_ssize_t offset) +/*[clinic end generated code: output=1042631674c6e0d3 input=6e80a5398e985025]*/ { - PyObject *s_object, *fmt, *newargs, *result; - Py_ssize_t n = PyTuple_GET_SIZE(args); + return Struct_unpack_from_impl(s_object, buffer, offset); +} - 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; +/*[clinic input] +iter_unpack - s_object = cache_struct(fmt); - if (s_object == NULL) { - Py_DECREF(newargs); - return NULL; - } - result = s_unpack_from(s_object, newargs, kwds); - Py_DECREF(newargs); - Py_DECREF(s_object); - return result; + format as s_object: cache_struct + 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_impl(PyObject *module, PyStructObject *s_object, + PyObject *buffer) +/*[clinic end generated code: output=0ae50e250d20e74d input=b214a58869a3c98d]*/ +{ + return Struct_iter_unpack(s_object, buffer); } static struct PyMethodDef module_functions[] = { - {"_clearcache", (PyCFunction)clearcache, METH_NOARGS, clearcache_doc}, - {"calcsize", calcsize, METH_O, calcsize_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)(void(*)(void))pack, METH_FASTCALL, pack_doc}, + {"pack_into", (PyCFunction)(void(*)(void))pack_into, METH_FASTCALL, pack_into_doc}, + UNPACK_METHODDEF + UNPACK_FROM_METHODDEF {NULL, NULL} /* sentinel */ }; @@ -1693,7 +2422,7 @@ these can be preceded by a decimal repeat count:\n\ x: pad byte (no data); c:char; b:signed byte; B:unsigned byte;\n\ ?: _Bool (requires C99; if not available, char is used instead)\n\ h:short; H:unsigned short; i:int; I:unsigned int;\n\ - l:long; L:unsigned long; f:float; d:double.\n\ + l:long; L:unsigned long; f:float; d:double; e:half-float.\n\ Special cases (preceding decimal count indicates length):\n\ s:string (array of char); p: pascal string (with count byte).\n\ Special cases (only available in native format):\n\ @@ -1718,7 +2447,6 @@ static struct PyModuleDef _structmodule = { NULL }; -extern "C" PyMODINIT_FUNC PyInit__pvt_struct(void) { @@ -1730,10 +2458,59 @@ PyInit__pvt_struct(void) if (m == NULL) return NULL; +#if PY_VERSION_HEX >= 0x03090000 + Py_SET_TYPE(&PyStructType, &PyType_Type); +#else Py_TYPE(&PyStructType) = &PyType_Type; +#endif if (PyType_Ready(&PyStructType) < 0) return NULL; + if (PyType_Ready(&unpackiter_type) < 0) + return NULL; + + /* Check endian and swap in faster functions */ + { + const formatdef *native = native_table; + formatdef *other, *ptr; +#if PY_LITTLE_ENDIAN + other = lilendian_table; +#else + other = bigendian_table; +#endif + /* Scan through the native table, find a matching + entry in the endian table and swap in the + native implementations whenever possible + (64-bit platforms may not have "standard" sizes) */ + while (native->format != '\0' && other->format != '\0') { + ptr = other; + while (ptr->format != '\0') { + if (ptr->format == native->format) { + /* Match faster when formats are + listed in the same order */ + if (ptr == other) + other++; + /* Only use the trick if the + size matches */ + if (ptr->size != native->size) + break; + /* Skip float and double, could be + "unknown" float format */ + if (ptr->format == 'd' || ptr->format == 'f') + break; + /* Skip _Bool, semantics are different for standard size */ + if (ptr->format == '?') + break; + ptr->pack = native->pack; + ptr->unpack = native->unpack; + break; + } + ptr++; + } + native++; + } + } + /* Add some symbolic constants to the module */ if (StructError == NULL) { StructError = PyErr_NewException("struct.error", NULL, NULL); @@ -1749,5 +2526,3 @@ PyInit__pvt_struct(void) return m; } - -// vim: fdm=marker diff --git a/src/wrapper/clinic_struct.c.h b/src/wrapper/clinic_struct.c.h new file mode 100644 index 00000000..36c4b404 --- /dev/null +++ b/src/wrapper/clinic_struct.c.h @@ -0,0 +1,389 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +PyDoc_STRVAR(Struct___init____doc__, +"Struct(format)\n" +"--\n" +"\n" +"Create a compiled struct object.\n" +"\n" +"Return a new Struct object which writes and reads binary data according to\n" +"the format string.\n" +"\n" +"See help(struct) for more on format strings."); + +static int +Struct___init___impl(PyStructObject *self, PyObject *format); + +static int +Struct___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + static const char * const _keywords[] = {"format", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "Struct", 0}; + PyObject *argsbuf[1]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + PyObject *format; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + if (!fastargs) { + goto exit; + } + format = fastargs[0]; + return_value = Struct___init___impl((PyStructObject *)self, format); + +exit: + return return_value; +} + +PyDoc_STRVAR(Struct_unpack__doc__, +"unpack($self, buffer, /)\n" +"--\n" +"\n" +"Return a tuple containing unpacked values.\n" +"\n" +"Unpack according to the format string Struct.format. The buffer\'s size\n" +"in bytes must be Struct.size.\n" +"\n" +"See help(struct) for more on format strings."); + +#define STRUCT_UNPACK_METHODDEF \ + {"unpack", (PyCFunction)Struct_unpack, METH_O, Struct_unpack__doc__}, + +static PyObject * +Struct_unpack_impl(PyStructObject *self, Py_buffer *buffer); + +static PyObject * +Struct_unpack(PyStructObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + Py_buffer buffer = {NULL, NULL}; + + if (PyObject_GetBuffer(arg, &buffer, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (!PyBuffer_IsContiguous(&buffer, 'C')) { + _PyArg_BadArgument("unpack", "argument", "contiguous buffer", arg); + goto exit; + } + return_value = Struct_unpack_impl(self, &buffer); + +exit: + /* Cleanup for buffer */ + if (buffer.obj) { + PyBuffer_Release(&buffer); + } + + return return_value; +} + +PyDoc_STRVAR(Struct_unpack_from__doc__, +"unpack_from($self, /, buffer, offset=0)\n" +"--\n" +"\n" +"Return a tuple containing unpacked values.\n" +"\n" +"Values are unpacked according to the format string Struct.format.\n" +"\n" +"The buffer\'s size in bytes, starting at position offset, must be\n" +"at least Struct.size.\n" +"\n" +"See help(struct) for more on format strings."); + +#define STRUCT_UNPACK_FROM_METHODDEF \ + {"unpack_from", (PyCFunction)(void(*)(void))Struct_unpack_from, METH_FASTCALL|METH_KEYWORDS, Struct_unpack_from__doc__}, + +static PyObject * +Struct_unpack_from_impl(PyStructObject *self, Py_buffer *buffer, + Py_ssize_t offset); + +static PyObject * +Struct_unpack_from(PyStructObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"buffer", "offset", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "unpack_from", 0}; + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_buffer buffer = {NULL, NULL}; + Py_ssize_t offset = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + if (!args) { + goto exit; + } + if (PyObject_GetBuffer(args[0], &buffer, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (!PyBuffer_IsContiguous(&buffer, 'C')) { + _PyArg_BadArgument("unpack_from", "argument 'buffer'", "contiguous buffer", args[0]); + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (PyFloat_Check(args[1])) { + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + offset = ival; + } +skip_optional_pos: + return_value = Struct_unpack_from_impl(self, &buffer, offset); + +exit: + /* Cleanup for buffer */ + if (buffer.obj) { + PyBuffer_Release(&buffer); + } + + return return_value; +} + +PyDoc_STRVAR(Struct_iter_unpack__doc__, +"iter_unpack($self, buffer, /)\n" +"--\n" +"\n" +"Return an iterator yielding tuples.\n" +"\n" +"Tuples are unpacked from the given bytes source, like a repeated\n" +"invocation of unpack_from().\n" +"\n" +"Requires that the bytes length be a multiple of the struct size."); + +#define STRUCT_ITER_UNPACK_METHODDEF \ + {"iter_unpack", (PyCFunction)Struct_iter_unpack, METH_O, Struct_iter_unpack__doc__}, + +PyDoc_STRVAR(_clearcache__doc__, +"_clearcache($module, /)\n" +"--\n" +"\n" +"Clear the internal cache."); + +#define _CLEARCACHE_METHODDEF \ + {"_clearcache", (PyCFunction)_clearcache, METH_NOARGS, _clearcache__doc__}, + +static PyObject * +_clearcache_impl(PyObject *module); + +static PyObject * +_clearcache(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _clearcache_impl(module); +} + +PyDoc_STRVAR(calcsize__doc__, +"calcsize($module, format, /)\n" +"--\n" +"\n" +"Return size in bytes of the struct described by the format string."); + +#define CALCSIZE_METHODDEF \ + {"calcsize", (PyCFunction)calcsize, METH_O, calcsize__doc__}, + +static Py_ssize_t +calcsize_impl(PyObject *module, PyStructObject *s_object); + +static PyObject * +calcsize(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyStructObject *s_object = NULL; + Py_ssize_t _return_value; + + if (!cache_struct_converter(arg, &s_object)) { + goto exit; + } + _return_value = calcsize_impl(module, s_object); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyLong_FromSsize_t(_return_value); + +exit: + /* Cleanup for s_object */ + Py_XDECREF(s_object); + + return return_value; +} + +PyDoc_STRVAR(unpack__doc__, +"unpack($module, format, buffer, /)\n" +"--\n" +"\n" +"Return a tuple containing values unpacked according to the format string.\n" +"\n" +"The buffer\'s size in bytes must be calcsize(format).\n" +"\n" +"See help(struct) for more on format strings."); + +#define UNPACK_METHODDEF \ + {"unpack", (PyCFunction)(void(*)(void))unpack, METH_FASTCALL, unpack__doc__}, + +static PyObject * +unpack_impl(PyObject *module, PyStructObject *s_object, Py_buffer *buffer); + +static PyObject * +unpack(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyStructObject *s_object = NULL; + Py_buffer buffer = {NULL, NULL}; + + if (!_PyArg_CheckPositional("unpack", nargs, 2, 2)) { + goto exit; + } + if (!cache_struct_converter(args[0], &s_object)) { + goto exit; + } + if (PyObject_GetBuffer(args[1], &buffer, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (!PyBuffer_IsContiguous(&buffer, 'C')) { + _PyArg_BadArgument("unpack", "argument 2", "contiguous buffer", args[1]); + goto exit; + } + return_value = unpack_impl(module, s_object, &buffer); + +exit: + /* Cleanup for s_object */ + Py_XDECREF(s_object); + /* Cleanup for buffer */ + if (buffer.obj) { + PyBuffer_Release(&buffer); + } + + return return_value; +} + +PyDoc_STRVAR(unpack_from__doc__, +"unpack_from($module, format, /, buffer, offset=0)\n" +"--\n" +"\n" +"Return a tuple containing values unpacked according to the format string.\n" +"\n" +"The buffer\'s size, minus offset, must be at least calcsize(format).\n" +"\n" +"See help(struct) for more on format strings."); + +#define UNPACK_FROM_METHODDEF \ + {"unpack_from", (PyCFunction)(void(*)(void))unpack_from, METH_FASTCALL|METH_KEYWORDS, unpack_from__doc__}, + +static PyObject * +unpack_from_impl(PyObject *module, PyStructObject *s_object, + Py_buffer *buffer, Py_ssize_t offset); + +static PyObject * +unpack_from(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"", "buffer", "offset", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "unpack_from", 0}; + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyStructObject *s_object = NULL; + Py_buffer buffer = {NULL, NULL}; + Py_ssize_t offset = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + if (!args) { + goto exit; + } + if (!cache_struct_converter(args[0], &s_object)) { + goto exit; + } + if (PyObject_GetBuffer(args[1], &buffer, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (!PyBuffer_IsContiguous(&buffer, 'C')) { + _PyArg_BadArgument("unpack_from", "argument 'buffer'", "contiguous buffer", args[1]); + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (PyFloat_Check(args[2])) { + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = PyNumber_Index(args[2]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + offset = ival; + } +skip_optional_pos: + return_value = unpack_from_impl(module, s_object, &buffer, offset); + +exit: + /* Cleanup for s_object */ + Py_XDECREF(s_object); + /* Cleanup for buffer */ + if (buffer.obj) { + PyBuffer_Release(&buffer); + } + + return return_value; +} + +PyDoc_STRVAR(iter_unpack__doc__, +"iter_unpack($module, format, buffer, /)\n" +"--\n" +"\n" +"Return an iterator yielding tuples unpacked from the given bytes.\n" +"\n" +"The bytes are unpacked according to the format string, like\n" +"a repeated invocation of unpack_from().\n" +"\n" +"Requires that the bytes length be a multiple of the format struct size."); + +#define ITER_UNPACK_METHODDEF \ + {"iter_unpack", (PyCFunction)(void(*)(void))iter_unpack, METH_FASTCALL, iter_unpack__doc__}, + +static PyObject * +iter_unpack_impl(PyObject *module, PyStructObject *s_object, + PyObject *buffer); + +static PyObject * +iter_unpack(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyStructObject *s_object = NULL; + PyObject *buffer; + + if (!_PyArg_CheckPositional("iter_unpack", nargs, 2, 2)) { + goto exit; + } + if (!cache_struct_converter(args[0], &s_object)) { + goto exit; + } + buffer = args[1]; + return_value = iter_unpack_impl(module, s_object, buffer); + +exit: + /* Cleanup for s_object */ + Py_XDECREF(s_object); + + return return_value; +} +/*[clinic end generated code: output=6a6228cfc4b7099c input=a9049054013a1b77]*/