Skip to content

Commit 9650ae6

Browse files
author
martin.v.loewis
committed
Patch #1610575: Add support for _Bool to struct.
git-svn-id: http://svn.python.org/projects/python/trunk@53508 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent c5c4d1b commit 9650ae6

7 files changed

Lines changed: 619 additions & 10 deletions

File tree

Doc/lib/libstruct.tex

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,15 @@ \section{\module{struct} ---
5050
\lineiv{c}{\ctype{char}}{string of length 1}{}
5151
\lineiv{b}{\ctype{signed char}}{integer}{}
5252
\lineiv{B}{\ctype{unsigned char}}{integer}{}
53+
\lineiv{t}{\ctype{_Bool}}{bool}{(1)}
5354
\lineiv{h}{\ctype{short}}{integer}{}
5455
\lineiv{H}{\ctype{unsigned short}}{integer}{}
5556
\lineiv{i}{\ctype{int}}{integer}{}
5657
\lineiv{I}{\ctype{unsigned int}}{long}{}
5758
\lineiv{l}{\ctype{long}}{integer}{}
5859
\lineiv{L}{\ctype{unsigned long}}{long}{}
59-
\lineiv{q}{\ctype{long long}}{long}{(1)}
60-
\lineiv{Q}{\ctype{unsigned long long}}{long}{(1)}
60+
\lineiv{q}{\ctype{long long}}{long}{(2)}
61+
\lineiv{Q}{\ctype{unsigned long long}}{long}{(2)}
6162
\lineiv{f}{\ctype{float}}{float}{}
6263
\lineiv{d}{\ctype{double}}{float}{}
6364
\lineiv{s}{\ctype{char[]}}{string}{}
@@ -70,6 +71,11 @@ \section{\module{struct} ---
7071

7172
\begin{description}
7273
\item[(1)]
74+
The \character{t} conversion code corresponds to the \ctype{_Bool} type
75+
defined by C99. If this type is not available, it is simulated using a
76+
\ctype{char}. In standard mode, it is always represented by one byte.
77+
\versionadded{2.6}
78+
\item[(2)]
7379
The \character{q} and \character{Q} conversion codes are available in
7480
native mode only if the platform C compiler supports C \ctype{long long},
7581
or, on Windows, \ctype{__int64}. They are always available in standard
@@ -118,6 +124,12 @@ \section{\module{struct} ---
118124
meaning a Python long integer will be used to hold the pointer; other
119125
platforms use 32-bit pointers and will use a Python integer.
120126

127+
For the \character{t} format character, the return value is either
128+
\constant{True} or \constant{False}. When packing, the truth value
129+
of the argument object is used. Either 0 or 1 in the native or standard
130+
bool representation will be packed, and any non-zero value will be True
131+
when unpacking.
132+
121133
By default, C numbers are represented in the machine's native format
122134
and byte order, and properly aligned by skipping pad bytes if
123135
necessary (according to the rules used by the C compiler).
@@ -151,6 +163,7 @@ \section{\module{struct} ---
151163
\ctype{long long} (\ctype{__int64} on Windows) is 8 bytes;
152164
\ctype{float} and \ctype{double} are 32-bit and 64-bit
153165
IEEE floating point numbers, respectively.
166+
\ctype{_Bool} is 1 byte.
154167

155168
Note the difference between \character{@} and \character{=}: both use
156169
native byte order, but the size and alignment of the latter is

Lib/test/test_struct.py

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ def deprecated_err(func, *args):
8484
if sz * 3 != struct.calcsize('iii'):
8585
raise TestFailed, 'inconsistent sizes'
8686

87-
fmt = 'cbxxxxxxhhhhiillffd'
88-
fmt3 = '3c3b18x12h6i6l6f3d'
87+
fmt = 'cbxxxxxxhhhhiillffdt'
88+
fmt3 = '3c3b18x12h6i6l6f3d3t'
8989
sz = struct.calcsize(fmt)
9090
sz3 = struct.calcsize(fmt3)
9191
if sz * 3 != sz3:
@@ -108,19 +108,21 @@ def deprecated_err(func, *args):
108108
l = 65536
109109
f = 3.1415
110110
d = 3.1415
111+
t = True
111112

112113
for prefix in ('', '@', '<', '>', '=', '!'):
113-
for format in ('xcbhilfd', 'xcBHILfd'):
114+
for format in ('xcbhilfdt', 'xcBHILfdt'):
114115
format = prefix + format
115116
if verbose:
116117
print "trying:", format
117-
s = struct.pack(format, c, b, h, i, l, f, d)
118-
cp, bp, hp, ip, lp, fp, dp = struct.unpack(format, s)
118+
s = struct.pack(format, c, b, h, i, l, f, d, t)
119+
cp, bp, hp, ip, lp, fp, dp, tp = struct.unpack(format, s)
119120
if (cp != c or bp != b or hp != h or ip != i or lp != l or
120-
int(100 * fp) != int(100 * f) or int(100 * dp) != int(100 * d)):
121+
int(100 * fp) != int(100 * f) or int(100 * dp) != int(100 * d) or
122+
tp != t):
121123
# ^^^ calculate only to two decimal places
122124
raise TestFailed, "unpack/pack not transitive (%s, %s)" % (
123-
str(format), str((cp, bp, hp, ip, lp, fp, dp)))
125+
str(format), str((cp, bp, hp, ip, lp, fp, dp, tp)))
124126

125127
# Test some of the new features in detail
126128

@@ -158,6 +160,11 @@ def deprecated_err(func, *args):
158160
('f', -2.0, '\300\000\000\000', '\000\000\000\300', 0),
159161
('d', -2.0, '\300\000\000\000\000\000\000\000',
160162
'\000\000\000\000\000\000\000\300', 0),
163+
('t', 0, '\0', '\0', 0),
164+
('t', 3, '\1', '\1', 1),
165+
('t', True, '\1', '\1', 0),
166+
('t', [], '\0', '\0', 1),
167+
('t', (1,), '\1', '\1', 1),
161168
]
162169

163170
for fmt, arg, big, lil, asy in tests:
@@ -612,3 +619,50 @@ def test_pack_into_fn():
612619
test_unpack_from()
613620
test_pack_into()
614621
test_pack_into_fn()
622+
623+
def test_bool():
624+
for prefix in tuple("<>!=")+('',):
625+
false = (), [], [], '', 0
626+
true = [1], 'test', 5, -1, 0xffffffffL+1, 0xffffffff/2
627+
628+
falseFormat = prefix + 't' * len(false)
629+
if verbose:
630+
print 'trying bool pack/unpack on', false, 'using format', falseFormat
631+
packedFalse = struct.pack(falseFormat, *false)
632+
unpackedFalse = struct.unpack(falseFormat, packedFalse)
633+
634+
trueFormat = prefix + 't' * len(true)
635+
if verbose:
636+
print 'trying bool pack/unpack on', true, 'using format', trueFormat
637+
packedTrue = struct.pack(trueFormat, *true)
638+
unpackedTrue = struct.unpack(trueFormat, packedTrue)
639+
640+
if len(true) != len(unpackedTrue):
641+
raise TestFailed('unpacked true array is not of same size as input')
642+
if len(false) != len(unpackedFalse):
643+
raise TestFailed('unpacked false array is not of same size as input')
644+
645+
for t in unpackedFalse:
646+
if t is not False:
647+
raise TestFailed('%r did not unpack as False' % t)
648+
for t in unpackedTrue:
649+
if t is not True:
650+
raise TestFailed('%r did not unpack as false' % t)
651+
652+
if prefix and verbose:
653+
print 'trying size of bool with format %r' % (prefix+'t')
654+
packed = struct.pack(prefix+'t', 1)
655+
656+
if len(packed) != struct.calcsize(prefix+'t'):
657+
raise TestFailed('packed length is not equal to calculated size')
658+
659+
if len(packed) != 1 and prefix:
660+
raise TestFailed('encoded bool is not one byte: %r' % packed)
661+
elif not prefix and verbose:
662+
print 'size of bool in native format is %i' % (len(packed))
663+
664+
for c in '\x01\x7f\xff\x0f\xf0':
665+
if struct.unpack('>t', c)[0] is not True:
666+
raise TestFailed('%c did not unpack as True' % c)
667+
668+
test_bool()

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ Library
313313
Extension Modules
314314
-----------------
315315

316+
- Patch #1610575: The struct module now supports the 't' code, for
317+
C99 _Bool.
318+
316319
- Patch #1635058: ensure that htonl and friends never accept or
317320
return negative numbers, per the underlying C implementation.
318321

Modules/_struct.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ typedef struct { char c; PY_LONG_LONG x; } s_long_long;
104104
#define LONG_LONG_ALIGN (sizeof(s_long_long) - sizeof(PY_LONG_LONG))
105105
#endif
106106

107+
#ifdef HAVE_C99_BOOL
108+
#define BOOL_TYPE _Bool
109+
typedef struct { char c; _Bool x; } s_bool;
110+
#define BOOL_ALIGN (sizeof(s_bool) - sizeof(BOOL_TYPE))
111+
#else
112+
#define BOOL_TYPE char
113+
#define BOOL_ALIGN 0
114+
#endif
115+
107116
#define STRINGIFY(x) #x
108117

109118
#ifdef __powerc
@@ -535,6 +544,15 @@ nu_ulonglong(const char *p, const formatdef *f)
535544

536545
#endif
537546

547+
static PyObject *
548+
nu_bool(const char *p, const formatdef *f)
549+
{
550+
BOOL_TYPE x;
551+
memcpy((char *)&x, p, sizeof x);
552+
return PyBool_FromLong(x != 0);
553+
}
554+
555+
538556
static PyObject *
539557
nu_float(const char *p, const formatdef *f)
540558
{
@@ -711,6 +729,16 @@ np_ulonglong(char *p, PyObject *v, const formatdef *f)
711729
}
712730
#endif
713731

732+
733+
static int
734+
np_bool(char *p, PyObject *v, const formatdef *f)
735+
{
736+
BOOL_TYPE y;
737+
y = PyObject_IsTrue(v);
738+
memcpy(p, (char *)&y, sizeof y);
739+
return 0;
740+
}
741+
714742
static int
715743
np_float(char *p, PyObject *v, const formatdef *f)
716744
{
@@ -771,6 +799,7 @@ static formatdef native_table[] = {
771799
{'q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_longlong, np_longlong},
772800
{'Q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_ulonglong,np_ulonglong},
773801
#endif
802+
{'t', sizeof(BOOL_TYPE), BOOL_ALIGN, nu_bool, np_bool},
774803
{'f', sizeof(float), FLOAT_ALIGN, nu_float, np_float},
775804
{'d', sizeof(double), DOUBLE_ALIGN, nu_double, np_double},
776805
{'P', sizeof(void *), VOID_P_ALIGN, nu_void_p, np_void_p},
@@ -865,6 +894,14 @@ bu_double(const char *p, const formatdef *f)
865894
return unpack_double(p, 0);
866895
}
867896

897+
static PyObject *
898+
bu_bool(const char *p, const formatdef *f)
899+
{
900+
char x;
901+
memcpy((char *)&x, p, sizeof x);
902+
return PyBool_FromLong(x != 0);
903+
}
904+
868905
static int
869906
bp_int(char *p, PyObject *v, const formatdef *f)
870907
{
@@ -969,6 +1006,15 @@ bp_double(char *p, PyObject *v, const formatdef *f)
9691006
return _PyFloat_Pack8(x, (unsigned char *)p, 0);
9701007
}
9711008

1009+
static int
1010+
bp_bool(char *p, PyObject *v, const formatdef *f)
1011+
{
1012+
char y;
1013+
y = PyObject_IsTrue(v);
1014+
memcpy(p, (char *)&y, sizeof y);
1015+
return 0;
1016+
}
1017+
9721018
static formatdef bigendian_table[] = {
9731019
{'x', 1, 0, NULL},
9741020
#ifdef PY_STRUCT_OVERFLOW_MASKING
@@ -990,6 +1036,7 @@ static formatdef bigendian_table[] = {
9901036
{'L', 4, 0, bu_uint, bp_uint},
9911037
{'q', 8, 0, bu_longlong, bp_longlong},
9921038
{'Q', 8, 0, bu_ulonglong, bp_ulonglong},
1039+
{'t', 1, 0, bu_bool, bp_bool},
9931040
{'f', 4, 0, bu_float, bp_float},
9941041
{'d', 8, 0, bu_double, bp_double},
9951042
{0}
@@ -1208,6 +1255,8 @@ static formatdef lilendian_table[] = {
12081255
{'L', 4, 0, lu_uint, lp_uint},
12091256
{'q', 8, 0, lu_longlong, lp_longlong},
12101257
{'Q', 8, 0, lu_ulonglong, lp_ulonglong},
1258+
{'t', 1, 0, bu_bool, bp_bool}, /* Std rep not endian dep,
1259+
but potentially different from native rep -- reuse bx_bool funcs. */
12111260
{'f', 4, 0, lu_float, lp_float},
12121261
{'d', 8, 0, lu_double, lp_double},
12131262
{0}

0 commit comments

Comments
 (0)