-from test_support import TestFailed, verbose
+from test_support import TestFailed, verbose, verify
import struct
## import pdb
func.__name__, args)
## pdb.set_trace()
-simple_err(struct.calcsize, 'Q')
+simple_err(struct.calcsize, 'Z')
sz = struct.calcsize('i')
if sz * 3 != struct.calcsize('iii'):
'\000\000\000\000\000\000\000\300', 0),
]
-def badpack(fmt, arg, got, exp):
- return
-
-def badunpack(fmt, arg, got, exp):
- return "unpack(%s, %s) -> (%s,) # expected (%s,)" % (
- `fmt`, `arg`, `got`, `exp`)
-
-isbigendian = struct.pack('=h', 1) == '\0\1'
+isbigendian = struct.pack('=i', 1)[0] == chr(0)
for fmt, arg, big, lil, asy in tests:
if verbose:
if rev != arg and not asy:
raise TestFailed, "unpack(%s, %s) -> (%s,) # expected (%s,)" % (
`fmt`, `res`, `rev`, `arg`)
+
+# Some q/Q sanity checks.
+
+has_native_qQ = 1
+try:
+ struct.pack("q", 5)
+except struct.error:
+ has_native_qQ = 0
+
+if verbose:
+ print "Platform has native q/Q?", has_native_qQ and "Yes." or "No."
+
+simple_err(struct.pack, "Q", -1) # can't pack -1 as unsigned regardless
+simple_err(struct.pack, "q", "a") # can't pack string as 'q' regardless
+simple_err(struct.pack, "Q", "a") # ditto, but 'Q'
+
+def force_bigendian(value):
+ if isbigendian:
+ return value
+ chars = list(value)
+ chars.reverse()
+ return "".join(chars)
+
+if has_native_qQ:
+ bytes = struct.calcsize('q')
+ # The expected values here are in big-endian format, primarily because
+ # I'm on a little-endian machine and so this is the clearest way (for
+ # me) to force the code to get exercised.
+ for format, input, expected in (
+ ('q', -1, '\xff' * bytes),
+ ('q', 0, '\x00' * bytes),
+ ('Q', 0, '\x00' * bytes),
+ ('q', 1L, '\x00' * (bytes-1) + '\x01'),
+ ('Q', (1L << (8*bytes))-1, '\xff' * bytes),
+ ('q', (1L << (8*bytes-1))-1, '\x7f' + '\xff' * (bytes - 1))):
+ got = struct.pack(format, input)
+ bigexpected = force_bigendian(expected)
+ verify(got == bigexpected,
+ "%r-pack of %r gave %r, not %r" %
+ (format, input, got, bigexpected))
+ retrieved = struct.unpack(format, got)[0]
+ verify(retrieved == input,
+ "%r-unpack of %r gave %r, not %r" %
+ (format, got, retrieved, input))
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 (w. count byte).\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.";
#define DOUBLE_ALIGN (sizeof(s_double) - sizeof(double))
#define VOID_P_ALIGN (sizeof(s_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; LONG_LONG x; } s_long_long;
+#define LONG_LONG_ALIGN (sizeof(s_long_long) - sizeof(LONG_LONG))
+
+#else
+static char qQ_error_msg[] =
+"q and Q unavailable in native mode on this platform; use a standard mode.\0";
+
+#endif
+
#define STRINGIFY(x) #x
#ifdef __powerc
}
}
+#ifdef HAVE_LONG_LONG
+
+/* Same, but handling native long long. */
+
+static int
+get_longlong(PyObject *v, LONG_LONG *p)
+{
+ LONG_LONG x;
+ int v_needs_decref = 0;
+
+ if (PyInt_Check(v)) {
+ x = (LONG_LONG)PyInt_AS_LONG(v);
+ *p = x;
+ return 0;
+ }
+ if (!PyLong_Check(v)) {
+ PyNumberMethods *m = v->ob_type->tp_as_number;
+ if (m != NULL && m->nb_long != NULL) {
+ v = m->nb_long(v);
+ if (v == NULL)
+ return -1;
+ v_needs_decref = 1;
+ }
+ if (!PyLong_Check(v)) {
+ PyErr_SetString(StructError,
+ "cannot convert argument to long");
+ if (v_needs_decref)
+ Py_DECREF(v);
+ return -1;
+ }
+ }
+ assert(PyLong_Check(v));
+ x = PyLong_AsLongLong(v);
+ if (v_needs_decref)
+ Py_DECREF(v);
+ if (x == (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 LONG_LONG *p)
+{
+ unsigned LONG_LONG x;
+ int v_needs_decref = 0;
+
+ if (PyInt_Check(v)) {
+ long i = PyInt_AS_LONG(v);
+ if (i < 0) {
+ PyErr_SetString(StructError, "can't convert negative "
+ "int to unsigned");
+ return -1;
+ }
+ x = (unsigned LONG_LONG)i;
+ *p = x;
+ return 0;
+ }
+ if (!PyLong_Check(v)) {
+ PyNumberMethods *m = v->ob_type->tp_as_number;
+ if (m != NULL && m->nb_long != NULL) {
+ v = m->nb_long(v);
+ if (v == NULL)
+ return -1;
+ v_needs_decref = 1;
+ }
+ if (!PyLong_Check(v)) {
+ PyErr_SetString(StructError,
+ "cannot convert argument to long");
+ if (v_needs_decref)
+ Py_DECREF(v);
+ return -1;
+ }
+ }
+ assert(PyLong_Check(v));
+ x = PyLong_AsUnsignedLongLong(v);
+ if (v_needs_decref)
+ Py_DECREF(v);
+ if (x == (unsigned LONG_LONG)-1 && PyErr_Occurred())
+ return -1;
+ *p = x;
+ return 0;
+}
+
+#endif
/* Floating point helpers */
const struct _formatdef *);
} formatdef;
+/* 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. */
+
static PyObject *
nu_char(const char *p, const formatdef *f)
{
return PyLong_FromUnsignedLong(*(unsigned long *)p);
}
+/* 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)
+{
+ return PyLong_FromLongLong(*(LONG_LONG *)p);
+}
+
+static PyObject *
+nu_ulonglong(const char *p, const formatdef *f)
+{
+ return PyLong_FromUnsignedLongLong(*(unsigned LONG_LONG *)p);
+}
+
+#else
+
+static PyObject *
+nu_qQerror(const char *p, const formatdef *f)
+{
+ PyErr_SetString(StructError, qQ_error_msg);
+ return NULL;
+}
+
+#endif
+
static PyObject *
nu_float(const char *p, const formatdef *f)
{
return 0;
}
+#ifdef HAVE_LONG_LONG
+
+static int
+np_longlong(char *p, PyObject *v, const formatdef *f)
+{
+ LONG_LONG x;
+ if (get_longlong(v, &x) < 0)
+ return -1;
+ * (LONG_LONG *)p = x;
+ return 0;
+}
+
+static int
+np_ulonglong(char *p, PyObject *v, const formatdef *f)
+{
+ unsigned LONG_LONG x;
+ if (get_ulonglong(v, &x) < 0)
+ return -1;
+ * (unsigned LONG_LONG *)p = x;
+ return 0;
+}
+
+#else
+
+static int
+np_qQerror(char *p, PyObject *v, const formatdef *f)
+{
+ PyErr_SetString(StructError, qQ_error_msg);
+ return -1;
+}
+
+#endif
+
static int
np_float(char *p, PyObject *v, const formatdef *f)
{
{'f', sizeof(float), FLOAT_ALIGN, nu_float, np_float},
{'d', sizeof(double), DOUBLE_ALIGN, nu_double, np_double},
{'P', sizeof(void *), VOID_P_ALIGN, nu_void_p, np_void_p},
+#ifdef HAVE_LONG_LONG
+ {'q', sizeof(LONG_LONG), LONG_LONG_ALIGN, nu_longlong, np_longlong},
+ {'Q', sizeof(LONG_LONG), LONG_LONG_ALIGN, nu_ulonglong,np_ulonglong},
+#else
+ /* n[pu]_qQerror just raise errors, but give them "the expected" size
+ and alignment anyway so that calcsize returns something reasonable,
+ and so unpack code that works on a 'long long' platform ends up in
+ the error routine instead of with a mysterious "unpack str size
+ does not match format" msg when run on a non-'long long' box. */
+ {'q', 8, 8, nu_qQerror, np_qQerror},
+ {'Q', 8, 8, nu_qQerror, np_qQerror},
+#endif
{0}
};