Kaydet (Commit) aef4c6bc authored tarafından Martin v. Löwis's avatar Martin v. Löwis

Patch #1610575: Add support for _Bool to struct.

üst 71cd5515
......@@ -50,14 +50,15 @@ C and Python values should be obvious given their types:
\lineiv{c}{\ctype{char}}{string of length 1}{}
\lineiv{b}{\ctype{signed char}}{integer}{}
\lineiv{B}{\ctype{unsigned char}}{integer}{}
\lineiv{t}{\ctype{_Bool}}{bool}{(1)}
\lineiv{h}{\ctype{short}}{integer}{}
\lineiv{H}{\ctype{unsigned short}}{integer}{}
\lineiv{i}{\ctype{int}}{integer}{}
\lineiv{I}{\ctype{unsigned int}}{long}{}
\lineiv{l}{\ctype{long}}{integer}{}
\lineiv{L}{\ctype{unsigned long}}{long}{}
\lineiv{q}{\ctype{long long}}{long}{(1)}
\lineiv{Q}{\ctype{unsigned long long}}{long}{(1)}
\lineiv{q}{\ctype{long long}}{long}{(2)}
\lineiv{Q}{\ctype{unsigned long long}}{long}{(2)}
\lineiv{f}{\ctype{float}}{float}{}
\lineiv{d}{\ctype{double}}{float}{}
\lineiv{s}{\ctype{char[]}}{string}{}
......@@ -70,6 +71,11 @@ Notes:
\begin{description}
\item[(1)]
The \character{t} conversion code corresponds to the \ctype{_Bool} type
defined by C99. If this type is not available, it is simulated using a
\ctype{char}. In standard mode, it is always represented by one byte.
\versionadded{2.6}
\item[(2)]
The \character{q} and \character{Q} conversion codes are available in
native mode only if the platform C compiler supports C \ctype{long long},
or, on Windows, \ctype{__int64}. They are always available in standard
......@@ -118,6 +124,12 @@ example, the Alpha and Merced processors use 64-bit pointer values,
meaning a Python long integer will be used to hold the pointer; other
platforms use 32-bit pointers and will use a Python integer.
For the \character{t} format character, the return value is either
\constant{True} or \constant{False}. When packing, the truth value
of the argument object is used. Either 0 or 1 in the native or standard
bool representation will be packed, and any non-zero value will be True
when unpacking.
By default, C numbers are represented in the machine's native format
and byte order, and properly aligned by skipping pad bytes if
necessary (according to the rules used by the C compiler).
......@@ -151,6 +163,7 @@ for any type (so you have to use pad bytes);
\ctype{long long} (\ctype{__int64} on Windows) is 8 bytes;
\ctype{float} and \ctype{double} are 32-bit and 64-bit
IEEE floating point numbers, respectively.
\ctype{_Bool} is 1 byte.
Note the difference between \character{@} and \character{=}: both use
native byte order, but the size and alignment of the latter is
......
......@@ -84,8 +84,8 @@ sz = struct.calcsize('i')
if sz * 3 != struct.calcsize('iii'):
raise TestFailed, 'inconsistent sizes'
fmt = 'cbxxxxxxhhhhiillffd'
fmt3 = '3c3b18x12h6i6l6f3d'
fmt = 'cbxxxxxxhhhhiillffdt'
fmt3 = '3c3b18x12h6i6l6f3d3t'
sz = struct.calcsize(fmt)
sz3 = struct.calcsize(fmt3)
if sz * 3 != sz3:
......@@ -108,19 +108,21 @@ i = 65535
l = 65536
f = 3.1415
d = 3.1415
t = True
for prefix in ('', '@', '<', '>', '=', '!'):
for format in ('xcbhilfd', 'xcBHILfd'):
for format in ('xcbhilfdt', 'xcBHILfdt'):
format = prefix + format
if verbose:
print "trying:", format
s = struct.pack(format, c, b, h, i, l, f, d)
cp, bp, hp, ip, lp, fp, dp = struct.unpack(format, s)
s = struct.pack(format, c, b, h, i, l, f, d, t)
cp, bp, hp, ip, lp, fp, dp, tp = struct.unpack(format, s)
if (cp != c or bp != b or hp != h or ip != i or lp != l or
int(100 * fp) != int(100 * f) or int(100 * dp) != int(100 * d)):
int(100 * fp) != int(100 * f) or int(100 * dp) != int(100 * d) or
tp != t):
# ^^^ calculate only to two decimal places
raise TestFailed, "unpack/pack not transitive (%s, %s)" % (
str(format), str((cp, bp, hp, ip, lp, fp, dp)))
str(format), str((cp, bp, hp, ip, lp, fp, dp, tp)))
# Test some of the new features in detail
......@@ -158,6 +160,11 @@ tests = [
('f', -2.0, '\300\000\000\000', '\000\000\000\300', 0),
('d', -2.0, '\300\000\000\000\000\000\000\000',
'\000\000\000\000\000\000\000\300', 0),
('t', 0, '\0', '\0', 0),
('t', 3, '\1', '\1', 1),
('t', True, '\1', '\1', 0),
('t', [], '\0', '\0', 1),
('t', (1,), '\1', '\1', 1),
]
for fmt, arg, big, lil, asy in tests:
......@@ -612,3 +619,50 @@ def test_pack_into_fn():
test_unpack_from()
test_pack_into()
test_pack_into_fn()
def test_bool():
for prefix in tuple("<>!=")+('',):
false = (), [], [], '', 0
true = [1], 'test', 5, -1, 0xffffffffL+1, 0xffffffff/2
falseFormat = prefix + 't' * len(false)
if verbose:
print 'trying bool pack/unpack on', false, 'using format', falseFormat
packedFalse = struct.pack(falseFormat, *false)
unpackedFalse = struct.unpack(falseFormat, packedFalse)
trueFormat = prefix + 't' * len(true)
if verbose:
print 'trying bool pack/unpack on', true, 'using format', trueFormat
packedTrue = struct.pack(trueFormat, *true)
unpackedTrue = struct.unpack(trueFormat, packedTrue)
if len(true) != len(unpackedTrue):
raise TestFailed('unpacked true array is not of same size as input')
if len(false) != len(unpackedFalse):
raise TestFailed('unpacked false array is not of same size as input')
for t in unpackedFalse:
if t is not False:
raise TestFailed('%r did not unpack as False' % t)
for t in unpackedTrue:
if t is not True:
raise TestFailed('%r did not unpack as false' % t)
if prefix and verbose:
print 'trying size of bool with format %r' % (prefix+'t')
packed = struct.pack(prefix+'t', 1)
if len(packed) != struct.calcsize(prefix+'t'):
raise TestFailed('packed length is not equal to calculated size')
if len(packed) != 1 and prefix:
raise TestFailed('encoded bool is not one byte: %r' % packed)
elif not prefix and verbose:
print 'size of bool in native format is %i' % (len(packed))
for c in '\x01\x7f\xff\x0f\xf0':
if struct.unpack('>t', c)[0] is not True:
raise TestFailed('%c did not unpack as True' % c)
test_bool()
......@@ -313,6 +313,9 @@ Library
Extension Modules
-----------------
- Patch #1610575: The struct module now supports the 't' code, for
C99 _Bool.
- Patch #1635058: ensure that htonl and friends never accept or
return negative numbers, per the underlying C implementation.
......
......@@ -104,6 +104,15 @@ typedef struct { char c; PY_LONG_LONG x; } s_long_long;
#define LONG_LONG_ALIGN (sizeof(s_long_long) - sizeof(PY_LONG_LONG))
#endif
#ifdef 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
#ifdef __powerc
......@@ -535,6 +544,15 @@ nu_ulonglong(const char *p, const formatdef *f)
#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)
{
......@@ -711,6 +729,16 @@ np_ulonglong(char *p, PyObject *v, const formatdef *f)
}
#endif
static int
np_bool(char *p, PyObject *v, const formatdef *f)
{
BOOL_TYPE y;
y = PyObject_IsTrue(v);
memcpy(p, (char *)&y, sizeof y);
return 0;
}
static int
np_float(char *p, PyObject *v, const formatdef *f)
{
......@@ -771,6 +799,7 @@ static formatdef native_table[] = {
{'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
{'t', 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},
{'P', sizeof(void *), VOID_P_ALIGN, nu_void_p, np_void_p},
......@@ -865,6 +894,14 @@ bu_double(const char *p, const formatdef *f)
return unpack_double(p, 0);
}
static PyObject *
bu_bool(const char *p, const formatdef *f)
{
char x;
memcpy((char *)&x, p, sizeof x);
return PyBool_FromLong(x != 0);
}
static int
bp_int(char *p, PyObject *v, const formatdef *f)
{
......@@ -969,6 +1006,15 @@ bp_double(char *p, PyObject *v, const formatdef *f)
return _PyFloat_Pack8(x, (unsigned char *)p, 0);
}
static int
bp_bool(char *p, PyObject *v, const formatdef *f)
{
char y;
y = PyObject_IsTrue(v);
memcpy(p, (char *)&y, sizeof y);
return 0;
}
static formatdef bigendian_table[] = {
{'x', 1, 0, NULL},
#ifdef PY_STRUCT_OVERFLOW_MASKING
......@@ -990,6 +1036,7 @@ static formatdef bigendian_table[] = {
{'L', 4, 0, bu_uint, bp_uint},
{'q', 8, 0, bu_longlong, bp_longlong},
{'Q', 8, 0, bu_ulonglong, bp_ulonglong},
{'t', 1, 0, bu_bool, bp_bool},
{'f', 4, 0, bu_float, bp_float},
{'d', 8, 0, bu_double, bp_double},
{0}
......@@ -1208,6 +1255,8 @@ static formatdef lilendian_table[] = {
{'L', 4, 0, lu_uint, lp_uint},
{'q', 8, 0, lu_longlong, lp_longlong},
{'Q', 8, 0, lu_ulonglong, lp_ulonglong},
{'t', 1, 0, bu_bool, bp_bool}, /* Std rep not endian dep,
but potentially different from native rep -- reuse bx_bool funcs. */
{'f', 4, 0, lu_float, lp_float},
{'d', 8, 0, lu_double, lp_double},
{0}
......
This diff is collapsed.
......@@ -1218,6 +1218,17 @@ if test "$have_long_long" = yes ; then
AC_CHECK_SIZEOF(long long, 8)
fi
AC_MSG_CHECKING(for _Bool support)
have_c99_bool=no
AC_TRY_COMPILE([], [_Bool x; x = (_Bool)0;], [
AC_DEFINE(HAVE_C99_BOOL, 1, [Define this if you have the type _Bool.])
have_c99_bool=yes
])
AC_MSG_RESULT($have_c99_bool)
if test "$have_c99_bool" = yes ; then
AC_CHECK_SIZEOF(_Bool, 1)
fi
AC_CHECK_TYPES(uintptr_t,
[AC_CHECK_SIZEOF(uintptr_t, 4)],
[], [#ifdef HAVE_STDINT_H
......
......@@ -64,6 +64,9 @@
/* Define if pthread_sigmask() does not work on your system. */
#undef HAVE_BROKEN_PTHREAD_SIGMASK
/* Define this if you have the type _Bool. */
#undef HAVE_C99_BOOL
/* Define to 1 if you have the `chown' function. */
#undef HAVE_CHOWN
......@@ -835,6 +838,9 @@
/* The size of a `wchar_t', as computed by sizeof. */
#undef SIZEOF_WCHAR_T
/* The size of a `_Bool', as computed by sizeof. */
#undef SIZEOF__BOOL
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment