📄 structmodule.c
字号:
/* Portions Copyright (c) 2005 Nokia Corporation */
/* Portions Copyright (c) 2005 Nokia Corporation */
/* struct module -- pack values into and (out of) strings */
/* New version supporting byte order, alignment and size options,
character strings, and unsigned numbers */
const static char struct__doc__[] = "\
Functions to convert between Python values and C structs.\n\
Python strings are used to hold the data representing the C struct\n\
and also as format strings to describe the layout of data in the C struct.\n\
\n\
The optional first format char indicates byte order, size and alignment:\n\
@: native order, size & alignment (default)\n\
=: native order, std. size & alignment\n\
<: little-endian, std. size & alignment\n\
>: big-endian, std. size & alignment\n\
!: same as >\n\
\n\
The remaining chars indicate types of args and must match exactly;\n\
these can be preceded by a decimal repeat count:\n\
x: pad byte (no data); c:char; b:signed byte; B:unsigned byte;\n\
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 (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.";
#include "Python.h"
#include <ctype.h>
/* Exception */
#ifndef SYMBIAN
static PyObject *StructError;
#else
#define StructError (PYTHON_GLOBALS->StructError)
#endif
/* 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; } st_short;
typedef struct { char c; int x; } st_int;
typedef struct { char c; long x; } st_long;
typedef struct { char c; float x; } st_float;
typedef struct { char c; double x; } st_double;
typedef struct { char c; void *x; } st_void_p;
#define SHORT_ALIGN (sizeof(st_short) - sizeof(short))
#define INT_ALIGN (sizeof(st_int) - sizeof(int))
#define LONG_ALIGN (sizeof(st_long) - sizeof(long))
#define FLOAT_ALIGN (sizeof(st_float) - sizeof(float))
#define DOUBLE_ALIGN (sizeof(st_double) - sizeof(double))
#define VOID_P_ALIGN (sizeof(st_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))
#endif
#define STRINGIFY(x) #x
#ifdef __powerc
#pragma options align=reset
#endif
/* Helper to get a PyLongObject by hook or by crook. Caller should decref. */
static PyObject *
get_pylong(PyObject *v)
{
PyNumberMethods *m;
assert(v != NULL);
if (PyInt_Check(v))
return PyLong_FromLong(PyInt_AS_LONG(v));
if (PyLong_Check(v)) {
Py_INCREF(v);
return v;
}
// XXX:CW32
m = (PyNumberMethods *)v->ob_type->tp_as_number;
if (m != NULL && m->nb_long != NULL) {
v = m->nb_long(v);
if (v == NULL)
return NULL;
if (PyLong_Check(v))
return v;
Py_DECREF(v);
}
PyErr_SetString(StructError,
"cannot convert argument to long");
return NULL;
}
/* Helper routine to get a Python integer and raise the appropriate error
if it isn't one */
static int
get_long(PyObject *v, long *p)
{
long x = PyInt_AsLong(v);
if (x == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(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(PyObject *v, unsigned long *p)
{
if (PyLong_Check(v)) {
unsigned long x = PyLong_AsUnsignedLong(v);
if (x == (unsigned long)(-1) && PyErr_Occurred())
return -1;
*p = x;
return 0;
}
else {
return get_long(v, (long *)p);
}
}
#ifdef HAVE_LONG_LONG
/* Same, but handling native long long. */
static int
get_longlong(PyObject *v, LONG_LONG *p)
{
LONG_LONG x;
v = get_pylong(v);
if (v == NULL)
return -1;
assert(PyLong_Check(v));
x = PyLong_AsLongLong(v);
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;
v = get_pylong(v);
if (v == NULL)
return -1;
assert(PyLong_Check(v));
x = PyLong_AsUnsignedLongLong(v);
Py_DECREF(v);
if (x == (unsigned LONG_LONG)-1 && PyErr_Occurred())
return -1;
*p = x;
return 0;
}
#endif
/* 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(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 if (!(e == 0 && f == 0.0)) {
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 = (char) (((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(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 if (!(e == 0 && f == 0.0)) {
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 = (char) (((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(const 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(const 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)(const char *,
const struct _formatdef *);
int (*pack)(char *, PyObject *,
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. ****************************************************/
/* NOTE:
In all n[up]_<type> routines handling types larger than 1 byte, there is
*no* guarantee that the p pointer is properly aligned for each type,
therefore memcpy is called. An intermediate variable is used to
compensate for big-endian architectures.
Normally both the intermediate variable and the memcpy call will be
skipped by C optimisation in little-endian architectures (gcc >= 2.91
does this). */
static PyObject *
nu_char(const char *p, const formatdef *f)
{
return PyString_FromStringAndSize(p, 1);
}
static PyObject *
nu_byte(const char *p, const formatdef *f)
{
return PyInt_FromLong((long) *(signed char *)p);
}
static PyObject *
nu_ubyte(const char *p, const formatdef *f)
{
return PyInt_FromLong((long) *(unsigned char *)p);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -