Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7b9542a

Browse files
committed
Initial support for 'q' and 'Q' struct format codes: for now, only in
native mode, and only when config #defines HAVE_LONG_LONG. Standard mode will eventually treat them as 8-byte ints across all platforms, but that likely requires a new set of routines in longobject.c first (while sizeof(long) >= 4 is guaranteed by C, there's nothing in C we can rely on x-platform to hold 8 bytes of int, so we'll have to roll our own; I'm thinking of a simple pair of conversion functions, Python long to/from sized vector of unsigned bytes; that may be useful for GMP conversions too; std q/Q would call them with size fixed at 8). test_struct.py: In addition to adding some native-mode 'q' and 'Q' tests, got rid of unused code, and repaired a non-portable assumption about native sizeof(short) (it isn't 2 on some Cray boxes). libstruct.tex: In addition to adding a bit of 'q'/'Q' docs (more needed later), removed an erroneous footnote about 'I' behavior.
1 parent fcc54ca commit 7b9542a

4 files changed

Lines changed: 247 additions & 16 deletions

File tree

Doc/lib/libstruct.tex

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@ \section{\module{struct} ---
5353
\lineiv{h}{\ctype{short}}{integer}{}
5454
\lineiv{H}{\ctype{unsigned short}}{integer}{}
5555
\lineiv{i}{\ctype{int}}{integer}{}
56-
\lineiv{I}{\ctype{unsigned int}}{long}{(1)}
56+
\lineiv{I}{\ctype{unsigned int}}{long}{}
5757
\lineiv{l}{\ctype{long}}{integer}{}
5858
\lineiv{L}{\ctype{unsigned long}}{long}{}
59+
\lineiv{q}{\ctype{long long}}{long}{(1)}
60+
\lineiv{Q}{\ctype{unsigned long long}}{long}{(1)}
5961
\lineiv{f}{\ctype{float}}{float}{}
6062
\lineiv{d}{\ctype{double}}{float}{}
6163
\lineiv{s}{\ctype{char[]}}{string}{}
@@ -68,10 +70,9 @@ \section{\module{struct} ---
6870

6971
\begin{description}
7072
\item[(1)]
71-
The \character{I} conversion code will convert to a Python long if
72-
the C \ctype{int} is the same size as a C \ctype{long}, which is
73-
typical on most modern systems. If a C \ctype{int} is smaller than
74-
a C \ctype{long}, an Python integer will be created instead.
73+
The \character{q} and \character{Q} conversion codes are available in
74+
native mode only if the platform C compiler supports C \ctype{long long},
75+
or, on Windows, \ctype{__int64}.
7576
\end{description}
7677

7778

Lib/test/test_struct.py

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from test_support import TestFailed, verbose
1+
from test_support import TestFailed, verbose, verify
22
import struct
33
## import pdb
44

@@ -12,7 +12,7 @@ def simple_err(func, *args):
1212
func.__name__, args)
1313
## pdb.set_trace()
1414

15-
simple_err(struct.calcsize, 'Q')
15+
simple_err(struct.calcsize, 'Z')
1616

1717
sz = struct.calcsize('i')
1818
if sz * 3 != struct.calcsize('iii'):
@@ -93,14 +93,7 @@ def simple_err(func, *args):
9393
'\000\000\000\000\000\000\000\300', 0),
9494
]
9595

96-
def badpack(fmt, arg, got, exp):
97-
return
98-
99-
def badunpack(fmt, arg, got, exp):
100-
return "unpack(%s, %s) -> (%s,) # expected (%s,)" % (
101-
`fmt`, `arg`, `got`, `exp`)
102-
103-
isbigendian = struct.pack('=h', 1) == '\0\1'
96+
isbigendian = struct.pack('=i', 1)[0] == chr(0)
10497

10598
for fmt, arg, big, lil, asy in tests:
10699
if verbose:
@@ -119,3 +112,47 @@ def badunpack(fmt, arg, got, exp):
119112
if rev != arg and not asy:
120113
raise TestFailed, "unpack(%s, %s) -> (%s,) # expected (%s,)" % (
121114
`fmt`, `res`, `rev`, `arg`)
115+
116+
# Some q/Q sanity checks.
117+
118+
has_native_qQ = 1
119+
try:
120+
struct.pack("q", 5)
121+
except struct.error:
122+
has_native_qQ = 0
123+
124+
if verbose:
125+
print "Platform has native q/Q?", has_native_qQ and "Yes." or "No."
126+
127+
simple_err(struct.pack, "Q", -1) # can't pack -1 as unsigned regardless
128+
simple_err(struct.pack, "q", "a") # can't pack string as 'q' regardless
129+
simple_err(struct.pack, "Q", "a") # ditto, but 'Q'
130+
131+
def force_bigendian(value):
132+
if isbigendian:
133+
return value
134+
chars = list(value)
135+
chars.reverse()
136+
return "".join(chars)
137+
138+
if has_native_qQ:
139+
bytes = struct.calcsize('q')
140+
# The expected values here are in big-endian format, primarily because
141+
# I'm on a little-endian machine and so this is the clearest way (for
142+
# me) to force the code to get exercised.
143+
for format, input, expected in (
144+
('q', -1, '\xff' * bytes),
145+
('q', 0, '\x00' * bytes),
146+
('Q', 0, '\x00' * bytes),
147+
('q', 1L, '\x00' * (bytes-1) + '\x01'),
148+
('Q', (1L << (8*bytes))-1, '\xff' * bytes),
149+
('q', (1L << (8*bytes-1))-1, '\x7f' + '\xff' * (bytes - 1))):
150+
got = struct.pack(format, input)
151+
bigexpected = force_bigendian(expected)
152+
verify(got == bigexpected,
153+
"%r-pack of %r gave %r, not %r" %
154+
(format, input, got, bigexpected))
155+
retrieved = struct.unpack(format, got)[0]
156+
verify(retrieved == input,
157+
"%r-unpack of %r gave %r, not %r" %
158+
(format, got, retrieved, input))

Misc/NEWS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,14 @@ Library
140140

141141
- pprint functions now much faster for large containers (tuple, list, dict).
142142

143+
- New 'q' and 'Q' format codes in the struct module, corresponding to C
144+
types "long long" and "unsigned long long" (on Windows, __int64). In
145+
native mode, these can be used only when the platform C compiler supports
146+
these types (when HAVE_LONG_LONG is #define'd by the Python config
147+
process), and then they inherit the sizes and alignments of the C types.
148+
XXX TODO In standard mode, 'q' and 'Q' are supported on all platforms, and
149+
XXX TODO are 8-byte integral types.
150+
143151
Tests
144152

145153
- New test_mutants.py runs dict comparisons where the key and value

Modules/structmodule.c

Lines changed: 186 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ these can be preceded by a decimal repeat count:\n\
2222
h:short; H:unsigned short; i:int; I:unsigned int;\n\
2323
l:long; L:unsigned long; f:float; d:double.\n\
2424
Special cases (preceding decimal count indicates length):\n\
25-
s:string (array of char); p: pascal string (w. count byte).\n\
25+
s:string (array of char); p: pascal string (with count byte).\n\
2626
Special case (only available in native format):\n\
2727
P:an integer type that is wide enough to hold a pointer.\n\
28+
Special case (not in native mode unless 'long long' in platform C):\n\
29+
q:long long; Q:unsigned long long\n\
2830
Whitespace between formats is ignored.\n\
2931
\n\
3032
The variable struct.error is an exception raised on errors.";
@@ -65,6 +67,18 @@ typedef struct { char c; void *x; } s_void_p;
6567
#define DOUBLE_ALIGN (sizeof(s_double) - sizeof(double))
6668
#define VOID_P_ALIGN (sizeof(s_void_p) - sizeof(void *))
6769

70+
/* We can't support q and Q in native mode unless the compiler does;
71+
in std mode, they're 8 bytes on all platforms. */
72+
#ifdef HAVE_LONG_LONG
73+
typedef struct { char c; LONG_LONG x; } s_long_long;
74+
#define LONG_LONG_ALIGN (sizeof(s_long_long) - sizeof(LONG_LONG))
75+
76+
#else
77+
static char qQ_error_msg[] =
78+
"q and Q unavailable in native mode on this platform; use a standard mode.\0";
79+
80+
#endif
81+
6882
#define STRINGIFY(x) #x
6983

7084
#ifdef __powerc
@@ -106,6 +120,93 @@ get_ulong(PyObject *v, unsigned long *p)
106120
}
107121
}
108122

123+
#ifdef HAVE_LONG_LONG
124+
125+
/* Same, but handling native long long. */
126+
127+
static int
128+
get_longlong(PyObject *v, LONG_LONG *p)
129+
{
130+
LONG_LONG x;
131+
int v_needs_decref = 0;
132+
133+
if (PyInt_Check(v)) {
134+
x = (LONG_LONG)PyInt_AS_LONG(v);
135+
*p = x;
136+
return 0;
137+
}
138+
if (!PyLong_Check(v)) {
139+
PyNumberMethods *m = v->ob_type->tp_as_number;
140+
if (m != NULL && m->nb_long != NULL) {
141+
v = m->nb_long(v);
142+
if (v == NULL)
143+
return -1;
144+
v_needs_decref = 1;
145+
}
146+
if (!PyLong_Check(v)) {
147+
PyErr_SetString(StructError,
148+
"cannot convert argument to long");
149+
if (v_needs_decref)
150+
Py_DECREF(v);
151+
return -1;
152+
}
153+
}
154+
assert(PyLong_Check(v));
155+
x = PyLong_AsLongLong(v);
156+
if (v_needs_decref)
157+
Py_DECREF(v);
158+
if (x == (LONG_LONG)-1 && PyErr_Occurred())
159+
return -1;
160+
*p = x;
161+
return 0;
162+
}
163+
164+
/* Same, but handling native unsigned long long. */
165+
166+
static int
167+
get_ulonglong(PyObject *v, unsigned LONG_LONG *p)
168+
{
169+
unsigned LONG_LONG x;
170+
int v_needs_decref = 0;
171+
172+
if (PyInt_Check(v)) {
173+
long i = PyInt_AS_LONG(v);
174+
if (i < 0) {
175+
PyErr_SetString(StructError, "can't convert negative "
176+
"int to unsigned");
177+
return -1;
178+
}
179+
x = (unsigned LONG_LONG)i;
180+
*p = x;
181+
return 0;
182+
}
183+
if (!PyLong_Check(v)) {
184+
PyNumberMethods *m = v->ob_type->tp_as_number;
185+
if (m != NULL && m->nb_long != NULL) {
186+
v = m->nb_long(v);
187+
if (v == NULL)
188+
return -1;
189+
v_needs_decref = 1;
190+
}
191+
if (!PyLong_Check(v)) {
192+
PyErr_SetString(StructError,
193+
"cannot convert argument to long");
194+
if (v_needs_decref)
195+
Py_DECREF(v);
196+
return -1;
197+
}
198+
}
199+
assert(PyLong_Check(v));
200+
x = PyLong_AsUnsignedLongLong(v);
201+
if (v_needs_decref)
202+
Py_DECREF(v);
203+
if (x == (unsigned LONG_LONG)-1 && PyErr_Occurred())
204+
return -1;
205+
*p = x;
206+
return 0;
207+
}
208+
209+
#endif
109210

110211
/* Floating point helpers */
111212

@@ -395,6 +496,17 @@ typedef struct _formatdef {
395496
const struct _formatdef *);
396497
} formatdef;
397498

499+
/* A large number of small routines follow, with names of the form
500+
501+
[bln][up]_TYPE
502+
503+
[bln] distiguishes among big-endian, little-endian and native.
504+
[pu] distiguishes between pack (to struct) and unpack (from struct).
505+
TYPE is one of char, byte, ubyte, etc.
506+
*/
507+
508+
/* Native mode routines. */
509+
398510
static PyObject *
399511
nu_char(const char *p, const formatdef *f)
400512
{
@@ -450,6 +562,34 @@ nu_ulong(const char *p, const formatdef *f)
450562
return PyLong_FromUnsignedLong(*(unsigned long *)p);
451563
}
452564

565+
/* Native mode doesn't support q or Q unless the platform C supports
566+
long long (or, on Windows, __int64). */
567+
568+
#ifdef HAVE_LONG_LONG
569+
570+
static PyObject *
571+
nu_longlong(const char *p, const formatdef *f)
572+
{
573+
return PyLong_FromLongLong(*(LONG_LONG *)p);
574+
}
575+
576+
static PyObject *
577+
nu_ulonglong(const char *p, const formatdef *f)
578+
{
579+
return PyLong_FromUnsignedLongLong(*(unsigned LONG_LONG *)p);
580+
}
581+
582+
#else
583+
584+
static PyObject *
585+
nu_qQerror(const char *p, const formatdef *f)
586+
{
587+
PyErr_SetString(StructError, qQ_error_msg);
588+
return NULL;
589+
}
590+
591+
#endif
592+
453593
static PyObject *
454594
nu_float(const char *p, const formatdef *f)
455595
{
@@ -585,6 +725,39 @@ np_ulong(char *p, PyObject *v, const formatdef *f)
585725
return 0;
586726
}
587727

728+
#ifdef HAVE_LONG_LONG
729+
730+
static int
731+
np_longlong(char *p, PyObject *v, const formatdef *f)
732+
{
733+
LONG_LONG x;
734+
if (get_longlong(v, &x) < 0)
735+
return -1;
736+
* (LONG_LONG *)p = x;
737+
return 0;
738+
}
739+
740+
static int
741+
np_ulonglong(char *p, PyObject *v, const formatdef *f)
742+
{
743+
unsigned LONG_LONG x;
744+
if (get_ulonglong(v, &x) < 0)
745+
return -1;
746+
* (unsigned LONG_LONG *)p = x;
747+
return 0;
748+
}
749+
750+
#else
751+
752+
static int
753+
np_qQerror(char *p, PyObject *v, const formatdef *f)
754+
{
755+
PyErr_SetString(StructError, qQ_error_msg);
756+
return -1;
757+
}
758+
759+
#endif
760+
588761
static int
589762
np_float(char *p, PyObject *v, const formatdef *f)
590763
{
@@ -642,6 +815,18 @@ static formatdef native_table[] = {
642815
{'f', sizeof(float), FLOAT_ALIGN, nu_float, np_float},
643816
{'d', sizeof(double), DOUBLE_ALIGN, nu_double, np_double},
644817
{'P', sizeof(void *), VOID_P_ALIGN, nu_void_p, np_void_p},
818+
#ifdef HAVE_LONG_LONG
819+
{'q', sizeof(LONG_LONG), LONG_LONG_ALIGN, nu_longlong, np_longlong},
820+
{'Q', sizeof(LONG_LONG), LONG_LONG_ALIGN, nu_ulonglong,np_ulonglong},
821+
#else
822+
/* n[pu]_qQerror just raise errors, but give them "the expected" size
823+
and alignment anyway so that calcsize returns something reasonable,
824+
and so unpack code that works on a 'long long' platform ends up in
825+
the error routine instead of with a mysterious "unpack str size
826+
does not match format" msg when run on a non-'long long' box. */
827+
{'q', 8, 8, nu_qQerror, np_qQerror},
828+
{'Q', 8, 8, nu_qQerror, np_qQerror},
829+
#endif
645830
{0}
646831
};
647832

0 commit comments

Comments
 (0)