| /*********************************************************** |
| Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam, |
| The Netherlands. |
| |
| All Rights Reserved |
| |
| Permission to use, copy, modify, and distribute this software and its |
| documentation for any purpose and without fee is hereby granted, |
| provided that the above copyright notice appear in all copies and that |
| both that copyright notice and this permission notice appear in |
| supporting documentation, and that the names of Stichting Mathematisch |
| Centrum or CWI or Corporation for National Research Initiatives or |
| CNRI not be used in advertising or publicity pertaining to |
| distribution of the software without specific, written prior |
| permission. |
| |
| While CWI is the initial source for this software, a modified version |
| is made available by the Corporation for National Research Initiatives |
| (CNRI) at the Internet address ftp://ftp.python.org. |
| |
| STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH |
| REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF |
| MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH |
| CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL |
| DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| PERFORMANCE OF THIS SOFTWARE. |
| |
| ******************************************************************/ |
| |
| /* struct module -- pack values into and (out of) strings */ |
| |
| /* New version supporting byte order, alignment and size options, |
| character strings, and unsigned numbers */ |
| |
| #include "Python.h" |
| #include "mymath.h" |
| |
| #include <limits.h> |
| |
| |
| /* Exception */ |
| |
| static PyObject *StructError; |
| |
| |
| /* Define various structs to figure out the alignments of types */ |
| |
| #ifdef __MWERKS__ |
| /* |
| ** XXXX We have a problem here. There are no unique alignment rules |
| ** on the PowerPC mac. |
| */ |
| #ifdef __powerc |
| #pragma options align=mac68k |
| #endif |
| #endif /* __MWERKS__ */ |
| |
| typedef struct { char c; short x; } s_short; |
| typedef struct { char c; int x; } s_int; |
| typedef struct { char c; long x; } s_long; |
| typedef struct { char c; float x; } s_float; |
| typedef struct { char c; double x; } s_double; |
| |
| #define SHORT_ALIGN (sizeof(s_short) - sizeof(short)) |
| #define INT_ALIGN (sizeof(s_int) - sizeof(int)) |
| #define LONG_ALIGN (sizeof(s_long) - sizeof(long)) |
| #define FLOAT_ALIGN (sizeof(s_float) - sizeof(float)) |
| #define DOUBLE_ALIGN (sizeof(s_double) - sizeof(double)) |
| |
| #ifdef __powerc |
| #pragma options align=reset |
| #endif |
| |
| |
| /* Global that will contain 2**<bits-per-long> */ |
| |
| static PyObject *offset = NULL; |
| |
| |
| /* Helper to create 2**<bits-per-long> */ |
| /* XXX This assumes 2's complement arithmetic */ |
| |
| static PyObject * |
| init_offset() |
| { |
| PyObject *result = NULL; |
| PyObject *one = PyLong_FromLong(1L); |
| PyObject *shiftcount = PyLong_FromLong(8 * sizeof(long)); |
| if (one == NULL || shiftcount == NULL) |
| goto finally; |
| result = PyNumber_Lshift(one, shiftcount); |
| finally: |
| Py_XDECREF(one); |
| Py_XDECREF(shiftcount); |
| return result; |
| } |
| |
| |
| /* Helper to add offset to a number */ |
| |
| static PyObject * |
| add_offset(v) |
| PyObject *v; |
| { |
| PyObject *result = NULL; |
| if (offset == NULL) { |
| if ((offset = init_offset()) == NULL) |
| goto finally; |
| } |
| result = PyNumber_Add(v, offset); |
| finally: |
| return result; |
| } |
| |
| |
| /* Same, but subtracting */ |
| |
| static PyObject * |
| sub_offset(v) |
| PyObject *v; |
| { |
| PyObject *result = NULL; |
| if (offset == NULL) { |
| if ((offset = init_offset()) == NULL) |
| goto finally; |
| } |
| result = PyNumber_Subtract(v, offset); |
| finally: |
| return result; |
| } |
| |
| |
| /* Helper routine to turn a (signed) long into an unsigned long */ |
| |
| static PyObject * |
| make_ulong(x) |
| long x; |
| { |
| PyObject *v = PyLong_FromLong(x); |
| if (x < 0 && v != NULL) { |
| PyObject *w = add_offset(v); |
| Py_DECREF(v); |
| return w; |
| } |
| else |
| return v; |
| } |
| |
| /* Helper routine to get a Python integer and raise the appropriate error |
| if it isn't one */ |
| |
| static int |
| get_long(v, p) |
| PyObject *v; |
| long *p; |
| { |
| long x = PyInt_AsLong(v); |
| if (x == -1 && PyErr_Occurred()) { |
| if (PyErr_Occurred() == PyExc_TypeError) |
| PyErr_SetString(StructError, |
| "required argument is not an integer"); |
| return -1; |
| } |
| *p = x; |
| return 0; |
| } |
| |
| |
| /* Same, but handling unsigned long */ |
| |
| static int |
| get_ulong(v, p) |
| PyObject *v; |
| unsigned long *p; |
| { |
| long x = PyInt_AsLong(v); |
| PyObject *exc; |
| if (x == -1 && (exc = PyErr_Occurred()) != NULL) { |
| if (exc == PyExc_OverflowError) { |
| /* Try again after subtracting offset */ |
| PyObject *w; |
| PyErr_Clear(); |
| if ((w = sub_offset(v)) == NULL) |
| return -1; |
| x = PyInt_AsLong(w); |
| Py_DECREF(w); |
| if (x != -1 || (exc = PyErr_Occurred()) == NULL) |
| goto okay; |
| } |
| if (exc == PyExc_TypeError) |
| PyErr_SetString(StructError, |
| "required argument is not an integer"); |
| else |
| return -1; |
| } |
| okay: |
| *p = x; |
| return 0; |
| } |
| |
| |
| /* Floating point helpers */ |
| |
| /* These use ANSI/IEEE Standard 754-1985 (Standard for Binary Floating |
| Point Arithmetic). See the following URL: |
| http://www.psc.edu/general/software/packages/ieee/ieee.html */ |
| |
| /* XXX Inf/NaN are not handled quite right (but underflow is!) */ |
| |
| static int |
| pack_float(x, p, incr) |
| double x; /* The number to pack */ |
| char *p; /* Where to pack the high order byte */ |
| int incr; /* 1 for big-endian; -1 for little-endian */ |
| { |
| int s; |
| int e; |
| double f; |
| long fbits; |
| |
| if (x < 0) { |
| s = 1; |
| x = -x; |
| } |
| else |
| s = 0; |
| |
| f = frexp(x, &e); |
| |
| /* Normalize f to be in the range [1.0, 2.0) */ |
| if (0.5 <= f && f < 1.0) { |
| f *= 2.0; |
| e--; |
| } |
| else if (f == 0.0) { |
| e = 0; |
| } |
| else { |
| PyErr_SetString(PyExc_SystemError, |
| "frexp() result out of range"); |
| return -1; |
| } |
| |
| if (e >= 128) { |
| /* XXX 128 itself is reserved for Inf/NaN */ |
| PyErr_SetString(PyExc_OverflowError, |
| "float too large to pack with f format"); |
| return -1; |
| } |
| else if (e < -126) { |
| /* Gradual underflow */ |
| f = ldexp(f, 126 + e); |
| e = 0; |
| } |
| else { |
| e += 127; |
| f -= 1.0; /* Get rid of leading 1 */ |
| } |
| |
| f *= 8388608.0; /* 2**23 */ |
| fbits = (long) floor(f + 0.5); /* Round */ |
| |
| /* First byte */ |
| *p = (s<<7) | (e>>1); |
| p += incr; |
| |
| /* Second byte */ |
| *p = ((e&1)<<7) | (fbits>>16); |
| p += incr; |
| |
| /* Third byte */ |
| *p = (fbits>>8) & 0xFF; |
| p += incr; |
| |
| /* Fourth byte */ |
| *p = fbits&0xFF; |
| |
| /* Done */ |
| return 0; |
| } |
| |
| static int |
| pack_double(x, p, incr) |
| double x; /* The number to pack */ |
| char *p; /* Where to pack the high order byte */ |
| int incr; /* 1 for big-endian; -1 for little-endian */ |
| { |
| int s; |
| int e; |
| double f; |
| long fhi, flo; |
| |
| if (x < 0) { |
| s = 1; |
| x = -x; |
| } |
| else |
| s = 0; |
| |
| f = frexp(x, &e); |
| |
| /* Normalize f to be in the range [1.0, 2.0) */ |
| if (0.5 <= f && f < 1.0) { |
| f *= 2.0; |
| e--; |
| } |
| else if (f == 0.0) { |
| e = 0; |
| } |
| else { |
| PyErr_SetString(PyExc_SystemError, |
| "frexp() result out of range"); |
| return -1; |
| } |
| |
| if (e >= 1024) { |
| /* XXX 1024 itself is reserved for Inf/NaN */ |
| PyErr_SetString(PyExc_OverflowError, |
| "float too large to pack with d format"); |
| return -1; |
| } |
| else if (e < -1022) { |
| /* Gradual underflow */ |
| f = ldexp(f, 1022 + e); |
| e = 0; |
| } |
| else { |
| e += 1023; |
| f -= 1.0; /* Get rid of leading 1 */ |
| } |
| |
| /* fhi receives the high 28 bits; flo the low 24 bits (== 52 bits) */ |
| f *= 268435456.0; /* 2**28 */ |
| fhi = (long) floor(f); /* Truncate */ |
| f -= (double)fhi; |
| f *= 16777216.0; /* 2**24 */ |
| flo = (long) floor(f + 0.5); /* Round */ |
| |
| /* First byte */ |
| *p = (s<<7) | (e>>4); |
| p += incr; |
| |
| /* Second byte */ |
| *p = ((e&0xF)<<4) | (fhi>>24); |
| p += incr; |
| |
| /* Third byte */ |
| *p = (fhi>>16) & 0xFF; |
| p += incr; |
| |
| /* Fourth byte */ |
| *p = (fhi>>8) & 0xFF; |
| p += incr; |
| |
| /* Fifth byte */ |
| *p = fhi & 0xFF; |
| p += incr; |
| |
| /* Sixth byte */ |
| *p = (flo>>16) & 0xFF; |
| p += incr; |
| |
| /* Seventh byte */ |
| *p = (flo>>8) & 0xFF; |
| p += incr; |
| |
| /* Eighth byte */ |
| *p = flo & 0xFF; |
| p += incr; |
| |
| /* Done */ |
| return 0; |
| } |
| |
| static PyObject * |
| unpack_float(p, incr) |
| char *p; /* Where the high order byte is */ |
| int incr; /* 1 for big-endian; -1 for little-endian */ |
| { |
| int s; |
| int e; |
| long f; |
| double x; |
| |
| /* First byte */ |
| s = (*p>>7) & 1; |
| e = (*p & 0x7F) << 1; |
| p += incr; |
| |
| /* Second byte */ |
| e |= (*p>>7) & 1; |
| f = (*p & 0x7F) << 16; |
| p += incr; |
| |
| /* Third byte */ |
| f |= (*p & 0xFF) << 8; |
| p += incr; |
| |
| /* Fourth byte */ |
| f |= *p & 0xFF; |
| |
| x = (double)f / 8388608.0; |
| |
| /* XXX This sadly ignores Inf/NaN issues */ |
| if (e == 0) |
| e = -126; |
| else { |
| x += 1.0; |
| e -= 127; |
| } |
| x = ldexp(x, e); |
| |
| if (s) |
| x = -x; |
| |
| return PyFloat_FromDouble(x); |
| } |
| |
| static PyObject * |
| unpack_double(p, incr) |
| char *p; /* Where the high order byte is */ |
| int incr; /* 1 for big-endian; -1 for little-endian */ |
| { |
| int s; |
| int e; |
| long fhi, flo; |
| double x; |
| |
| /* First byte */ |
| s = (*p>>7) & 1; |
| e = (*p & 0x7F) << 4; |
| p += incr; |
| |
| /* Second byte */ |
| e |= (*p>>4) & 0xF; |
| fhi = (*p & 0xF) << 24; |
| p += incr; |
| |
| /* Third byte */ |
| fhi |= (*p & 0xFF) << 16; |
| p += incr; |
| |
| /* Fourth byte */ |
| fhi |= (*p & 0xFF) << 8; |
| p += incr; |
| |
| /* Fifth byte */ |
| fhi |= *p & 0xFF; |
| p += incr; |
| |
| /* Sixth byte */ |
| flo = (*p & 0xFF) << 16; |
| p += incr; |
| |
| /* Seventh byte */ |
| flo |= (*p & 0xFF) << 8; |
| p += incr; |
| |
| /* Eighth byte */ |
| flo |= *p & 0xFF; |
| p += incr; |
| |
| x = (double)fhi + (double)flo / 16777216.0; /* 2**24 */ |
| x /= 268435456.0; /* 2**28 */ |
| |
| /* XXX This sadly ignores Inf/NaN */ |
| if (e == 0) |
| e = -1022; |
| else { |
| x += 1.0; |
| e -= 1023; |
| } |
| x = ldexp(x, e); |
| |
| if (s) |
| x = -x; |
| |
| return PyFloat_FromDouble(x); |
| } |
| |
| |
| /* The translation function for each format character is table driven */ |
| |
| typedef struct _formatdef { |
| char format; |
| int size; |
| int alignment; |
| PyObject* (*unpack) Py_PROTO((const char *, |
| const struct _formatdef *)); |
| int (*pack) Py_PROTO((char *, |
| PyObject *, |
| const struct _formatdef *)); |
| } formatdef; |
| |
| static PyObject * |
| nu_char(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| return PyString_FromStringAndSize(p, 1); |
| } |
| |
| static PyObject * |
| nu_byte(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| return PyInt_FromLong((long) *(signed char *)p); |
| } |
| |
| static PyObject * |
| nu_ubyte(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| return PyInt_FromLong((long) *(unsigned char *)p); |
| } |
| |
| static PyObject * |
| nu_short(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| return PyInt_FromLong((long) *(short *)p); |
| } |
| |
| static PyObject * |
| nu_ushort(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| return PyInt_FromLong((long) *(unsigned short *)p); |
| } |
| |
| static PyObject * |
| nu_int(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| return PyInt_FromLong((long) *(int *)p); |
| } |
| |
| static PyObject * |
| nu_uint(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| unsigned int x = *(unsigned int *)p; |
| #if INT_MAX == LONG_MAX |
| return make_ulong((long)x); |
| #else |
| return PyInt_FromLong((long)x); |
| #endif |
| } |
| |
| static PyObject * |
| nu_long(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| return PyInt_FromLong(*(long *)p); |
| } |
| |
| static PyObject * |
| nu_ulong(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| return make_ulong(*(long *)p); |
| } |
| |
| static PyObject * |
| nu_float(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| float x; |
| memcpy((char *)&x, p, sizeof(float)); |
| return PyFloat_FromDouble((double)x); |
| } |
| |
| static PyObject * |
| nu_double(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| double x; |
| memcpy((char *)&x, p, sizeof(double)); |
| return PyFloat_FromDouble(x); |
| } |
| |
| static int |
| np_byte(p, v, f) |
| char *p; |
| PyObject *v; |
| const formatdef *f; |
| { |
| long x; |
| if (get_long(v, &x) < 0) |
| return -1; |
| *p = x; |
| return 0; |
| } |
| |
| static int |
| np_char(p, v, f) |
| 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(p, v, f) |
| char *p; |
| PyObject *v; |
| const formatdef *f; |
| { |
| long x; |
| if (get_long(v, &x) < 0) |
| return -1; |
| * (short *)p = x; |
| return 0; |
| } |
| |
| static int |
| np_int(p, v, f) |
| char *p; |
| PyObject *v; |
| const formatdef *f; |
| { |
| long x; |
| if (get_long(v, &x) < 0) |
| return -1; |
| * (int *)p = x; |
| return 0; |
| } |
| |
| static int |
| np_uint(p, v, f) |
| char *p; |
| PyObject *v; |
| const formatdef *f; |
| { |
| unsigned long x; |
| if (get_ulong(v, &x) < 0) |
| return -1; |
| * (unsigned int *)p = x; |
| return 0; |
| } |
| |
| static int |
| np_long(p, v, f) |
| char *p; |
| PyObject *v; |
| const formatdef *f; |
| { |
| long x; |
| if (get_long(v, &x) < 0) |
| return -1; |
| * (long *)p = x; |
| return 0; |
| } |
| |
| static int |
| np_ulong(p, v, f) |
| char *p; |
| PyObject *v; |
| const formatdef *f; |
| { |
| unsigned long x; |
| if (get_ulong(v, &x) < 0) |
| return -1; |
| * (unsigned long *)p = x; |
| return 0; |
| } |
| |
| static int |
| np_float(p, v, f) |
| 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(float)); |
| return 0; |
| } |
| |
| static int |
| np_double(p, v, f) |
| 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 formatdef native_table[] = { |
| {'x', sizeof(char), 0, NULL}, |
| {'b', sizeof(char), 0, nu_byte, np_byte}, |
| {'B', sizeof(char), 0, nu_ubyte, np_byte}, |
| {'c', sizeof(char), 0, nu_char, np_char}, |
| {'s', sizeof(char), 0, NULL}, |
| {'h', sizeof(short), SHORT_ALIGN, nu_short, np_short}, |
| {'H', sizeof(short), SHORT_ALIGN, nu_ushort, np_short}, |
| {'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}, |
| {'f', sizeof(float), FLOAT_ALIGN, nu_float, np_float}, |
| {'d', sizeof(double), DOUBLE_ALIGN, nu_double, np_double}, |
| {0} |
| }; |
| |
| static PyObject * |
| bu_int(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| long x = 0; |
| int i = f->size; |
| do { |
| x = (x<<8) | (*p++ & 0xFF); |
| } while (--i > 0); |
| i = 8*(sizeof(long) - f->size); |
| if (i) { |
| x <<= i; |
| x >>= i; |
| } |
| return PyInt_FromLong(x); |
| } |
| |
| static PyObject * |
| bu_uint(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| long x = 0; |
| int i = f->size; |
| do { |
| x = (x<<8) | (*p++ & 0xFF); |
| } while (--i > 0); |
| if (f->size == sizeof(long)) |
| return make_ulong(x); |
| else |
| return PyLong_FromLong(x); |
| } |
| |
| static PyObject * |
| bu_float(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| return unpack_float(p, 1); |
| } |
| |
| static PyObject * |
| bu_double(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| return unpack_double(p, 1); |
| } |
| |
| static int |
| bp_int(p, v, f) |
| char *p; |
| PyObject *v; |
| const formatdef *f; |
| { |
| long x; |
| int i; |
| if (get_long(v, &x) < 0) |
| return -1; |
| i = f->size; |
| do { |
| p[--i] = x; |
| x >>= 8; |
| } while (i > 0); |
| return 0; |
| } |
| |
| static int |
| bp_uint(p, v, f) |
| char *p; |
| PyObject *v; |
| const formatdef *f; |
| { |
| unsigned long x; |
| int i; |
| if (get_ulong(v, &x) < 0) |
| return -1; |
| i = f->size; |
| do { |
| p[--i] = x; |
| x >>= 8; |
| } while (i > 0); |
| return 0; |
| } |
| |
| static int |
| bp_float(p, v, f) |
| 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; |
| } |
| return pack_float(x, p, 1); |
| } |
| |
| static int |
| bp_double(p, v, f) |
| 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; |
| } |
| return pack_double(x, p, 1); |
| } |
| |
| static formatdef bigendian_table[] = { |
| {'x', 1, 0, NULL}, |
| {'b', 1, 0, bu_int, bp_int}, |
| {'B', 1, 0, bu_uint, bp_int}, |
| {'c', 1, 0, nu_char, np_char}, |
| {'s', 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}, |
| {'f', 4, 0, bu_float, bp_float}, |
| {'d', 8, 0, bu_double, bp_double}, |
| {0} |
| }; |
| |
| static PyObject * |
| lu_int(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| long x = 0; |
| int i = f->size; |
| do { |
| x = (x<<8) | (p[--i] & 0xFF); |
| } while (i > 0); |
| i = 8*(sizeof(long) - f->size); |
| if (i) { |
| x <<= i; |
| x >>= i; |
| } |
| return PyInt_FromLong(x); |
| } |
| |
| static PyObject * |
| lu_uint(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| long x = 0; |
| int i = f->size; |
| do { |
| x = (x<<8) | (p[--i] & 0xFF); |
| } while (i > 0); |
| if (f->size == sizeof(long)) |
| return make_ulong(x); |
| else |
| return PyLong_FromLong(x); |
| } |
| |
| static PyObject * |
| lu_float(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| return unpack_float(p+3, -1); |
| } |
| |
| static PyObject * |
| lu_double(p, f) |
| const char *p; |
| const formatdef *f; |
| { |
| return unpack_double(p+7, -1); |
| } |
| |
| static int |
| lp_int(p, v, f) |
| char *p; |
| PyObject *v; |
| const formatdef *f; |
| { |
| long x; |
| int i; |
| if (get_long(v, &x) < 0) |
| return -1; |
| i = f->size; |
| do { |
| *p++ = x; |
| x >>= 8; |
| } while (--i > 0); |
| return 0; |
| } |
| |
| static int |
| lp_uint(p, v, f) |
| char *p; |
| PyObject *v; |
| const formatdef *f; |
| { |
| unsigned long x; |
| int i; |
| if (get_ulong(v, &x) < 0) |
| return -1; |
| i = f->size; |
| do { |
| *p++ = x; |
| x >>= 8; |
| } while (--i > 0); |
| return 0; |
| } |
| |
| static int |
| lp_float(p, v, f) |
| 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; |
| } |
| return pack_float(x, p+3, -1); |
| } |
| |
| static int |
| lp_double(p, v, f) |
| 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; |
| } |
| return pack_double(x, p+7, -1); |
| } |
| |
| static formatdef lilendian_table[] = { |
| {'x', 1, 0, NULL}, |
| {'b', 1, 0, lu_int, lp_int}, |
| {'B', 1, 0, lu_uint, lp_int}, |
| {'c', 1, 0, nu_char, np_char}, |
| {'s', 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}, |
| {'f', 4, 0, lu_float, lp_float}, |
| {'d', 8, 0, lu_double, lp_double}, |
| {0} |
| }; |
| |
| |
| static const formatdef * |
| whichtable(pfmt) |
| 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 aligment! */ |
| int n = 1; |
| char *p = (char *) &n; |
| if (*p == 1) |
| return lilendian_table; |
| else |
| return bigendian_table; |
| } |
| default: |
| --*pfmt; /* Back out of pointer increment */ |
| /* Fall through */ |
| case '@': |
| return native_table; |
| } |
| } |
| |
| |
| /* Get the table entry for a format code */ |
| |
| static const formatdef * |
| getentry(c, f) |
| 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 int |
| align(size, c, e) |
| int size; |
| int 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 |
| calcsize(fmt, f) |
| const char *fmt; |
| const formatdef *f; |
| { |
| const formatdef *e; |
| const char *s; |
| char c; |
| int size, num, itemsize, x; |
| |
| s = fmt; |
| size = 0; |
| while ((c = *s++) != '\0') { |
| 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; |
| 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; |
| } |
| } |
| |
| return size; |
| } |
| |
| |
| /* pack(fmt, v1, v2, ...) --> string */ |
| |
| static PyObject * |
| struct_calcsize(self, args) |
| PyObject *self; /* Not used */ |
| PyObject *args; |
| { |
| char *fmt; |
| const formatdef *f; |
| int size; |
| |
| if (!PyArg_ParseTuple(args, "s", &fmt)) |
| return NULL; |
| f = whichtable(&fmt); |
| size = calcsize(fmt, f); |
| if (size < 0) |
| return NULL; |
| return PyInt_FromLong((long)size); |
| } |
| |
| |
| /* pack(fmt, v1, v2, ...) --> string */ |
| |
| static PyObject * |
| struct_pack(self, args) |
| PyObject *self; /* Not used */ |
| PyObject *args; |
| { |
| const formatdef *f, *e; |
| PyObject *format, *result, *v; |
| char *fmt; |
| int size, num; |
| int i, n; |
| char *s, *res, *restart, *nres; |
| char c; |
| |
| if (args == NULL || !PyTuple_Check(args) || |
| (n = PyTuple_Size(args)) < 1) |
| { |
| PyErr_BadArgument(); |
| return NULL; |
| } |
| format = PyTuple_GetItem(args, 0); |
| if (!PyArg_Parse(format, "s", &fmt)) |
| return NULL; |
| f = whichtable(&fmt); |
| size = calcsize(fmt, f); |
| if (size < 0) |
| return NULL; |
| result = PyString_FromStringAndSize((char *)NULL, size); |
| if (result == NULL) |
| return NULL; |
| |
| s = fmt; |
| i = 1; |
| res = restart = PyString_AsString(result); |
| |
| while ((c = *s++) != '\0') { |
| 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); |
| if (e == NULL) |
| goto fail; |
| nres = restart + align((int)(res-restart), c, e); |
| /* Fill padd bytes with zeros */ |
| while (res < nres) |
| *res++ = '\0'; |
| if (num == 0 && c != 's') |
| continue; |
| do { |
| if (c == 'x') { |
| /* doesn't consume arguments */ |
| memset(res, '\0', num); |
| res += num; |
| break; |
| } |
| if (i >= n) { |
| PyErr_SetString(StructError, |
| "insufficient arguments to pack"); |
| goto fail; |
| } |
| v = PyTuple_GetItem(args, i++); |
| if (v == NULL) |
| goto fail; |
| if (c == 's') { |
| /* num is string size, not repeat count */ |
| int n; |
| if (!PyString_Check(v)) { |
| PyErr_SetString(StructError, |
| "argument for 's' must be a string"); |
| goto fail; |
| } |
| n = PyString_Size(v); |
| if (n > num) |
| n = num; |
| if (n > 0) |
| memcpy(res, PyString_AsString(v), n); |
| if (n < num) |
| memset(res+n, '\0', num-n); |
| res += num; |
| break; |
| } |
| else { |
| if (e->pack(res, v, e) < 0) |
| goto fail; |
| res += e->size; |
| } |
| } while (--num > 0); |
| } |
| |
| if (i < n) { |
| PyErr_SetString(StructError, |
| "too many arguments for pack format"); |
| goto fail; |
| } |
| |
| return result; |
| |
| fail: |
| Py_DECREF(result); |
| return NULL; |
| } |
| |
| |
| /* unpack(fmt, string) --> (v1, v2, ...) */ |
| |
| static PyObject * |
| struct_unpack(self, args) |
| PyObject *self; /* Not used */ |
| PyObject *args; |
| { |
| const formatdef *f, *e; |
| char *str, *start, *fmt, *s; |
| char c; |
| int len, size, num; |
| PyObject *res, *v; |
| |
| if (!PyArg_ParseTuple(args, "ss#", &fmt, &start, &len)) |
| return NULL; |
| f = whichtable(&fmt); |
| size = calcsize(fmt, f); |
| if (size < 0) |
| return NULL; |
| if (size != len) { |
| PyErr_SetString(StructError, |
| "unpack str size does not match format"); |
| return NULL; |
| } |
| res = PyList_New(0); |
| if (res == NULL) |
| return NULL; |
| str = start; |
| s = fmt; |
| while ((c = *s++) != '\0') { |
| 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); |
| if (e == NULL) |
| goto fail; |
| str = start + align((int)(str-start), c, e); |
| if (num == 0 && c != 's') |
| continue; |
| |
| do { |
| if (c == 'x') { |
| str += num; |
| break; |
| } |
| if (c == 's') { |
| /* num is string size, not repeat count */ |
| v = PyString_FromStringAndSize(str, num); |
| if (v == NULL) |
| goto fail; |
| str += num; |
| num = 0; |
| } |
| else { |
| v = e->unpack(str, e); |
| if (v == NULL) |
| goto fail; |
| str += e->size; |
| } |
| if (v == NULL || PyList_Append(res, v) < 0) |
| goto fail; |
| Py_DECREF(v); |
| } while (--num > 0); |
| } |
| |
| v = PyList_AsTuple(res); |
| Py_DECREF(res); |
| return v; |
| |
| fail: |
| Py_DECREF(res); |
| return NULL; |
| } |
| |
| |
| /* List of functions */ |
| |
| static PyMethodDef struct_methods[] = { |
| {"calcsize", struct_calcsize, METH_VARARGS}, |
| {"pack", struct_pack, METH_VARARGS}, |
| {"unpack", struct_unpack, METH_VARARGS}, |
| {NULL, NULL} /* sentinel */ |
| }; |
| |
| |
| /* Module initialization */ |
| |
| void |
| initstruct() |
| { |
| PyObject *m, *d; |
| |
| /* Create the module and add the functions */ |
| m = Py_InitModule("struct", struct_methods); |
| |
| /* Add some symbolic constants to the module */ |
| d = PyModule_GetDict(m); |
| StructError = PyString_FromString("struct.error"); |
| PyDict_SetItemString(d, "error", StructError); |
| |
| /* Check for errors */ |
| if (PyErr_Occurred()) |
| Py_FatalError("can't initialize module struct"); |
| } |