Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(548)

Unified Diff: Modules/_struct.c

Issue 3863042: Implement PEP 3118 struct changes Base URL: http://svn.python.org/view/*checkout*/python/branches/py3k/
Patch Set: Created 13 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« Lib/test/test_struct.py ('K') | « Lib/test/test_struct.py ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Modules/_struct.c
===================================================================
--- Modules/_struct.c (revision 87813)
+++ Modules/_struct.c (working copy)
@@ -22,19 +22,67 @@
const struct _formatdef *);
} formatdef;
-typedef struct _formatcode {
+
+/* A left child / right sibling tree used to represent the structure
+ of the format codes. Child nodes are added to represent nested
+ struct formats. */
+
+typedef struct _formattree {
+ Py_ssize_t s_size;
+ Py_ssize_t s_len;
+ Py_ssize_t s_count;
+ Py_ssize_t s_offset;
+ Py_ssize_t s_alignment;
const struct _formatdef *fmtdef;
- Py_ssize_t offset;
- Py_ssize_t size;
-} formatcode;
+ struct _formattree *child;
+ struct _formattree *sibling;
+} formattree;
+
+#define FormatTree_HasChildren(ft) ((ft)->fmtdef == 0)
+#define FormatTree_AppendChild(ft, new_child) \
+ do { \
+ if ((ft)->child == NULL) { \
+ (ft)->child = (new_child); \
+ } else { \
+ formattree *_t = (ft)->child; \
+ for ( ; _t->sibling != NULL; _t = _t->sibling); \
+ _t->sibling = (new_child); \
+ } \
+ } while (0)
+#define FormatTree_RemoveChild(ft, rmchild) \
+ do { \
+ if ((ft)->child && (ft)->child == rmchild) { \
+ (ft)->child = rmchild->sibling; \
+ } else { \
+ formattree *t = (ft)->child; \
+ for ( ; t->sibling != NULL \
+ && t->sibling != rmchild; t = t->sibling); \
+ if (t->sibling == rmchild) \
+ t->sibling = rmchild->sibling; \
+ } \
+ } while (0)
+
+#define FormatTree_IsPadding(ft) (((ft)->fmtdef != 0) && \
+ ((ft)->fmtdef->format == 'x'))
+
+#define MAX_STRUCT_DEPTH 64
+
+/* Holds the state of the struct string parser. */
+
+typedef struct _parser_state {
+ const formatdef *byte_fmt;
+ const char *fmt;
+ Py_ssize_t error_cnt;
+ Py_ssize_t struct_depth;
+} parser_state;
+
+
/* Struct object interface */
typedef struct {
PyObject_HEAD
- Py_ssize_t s_size;
- Py_ssize_t s_len;
- formatcode *s_codes;
+ formattree *s_tree;
PyObject *s_format;
PyObject *weakreflist; /* List of weak references */
} PyStructObject;
@@ -83,14 +131,78 @@
#endif
#define STRINGIFY(x) #x
+#define MAX(x, y) ((x) < (y) ? (y) : (x))
#ifdef __powerc
#pragma options align=reset
#endif
+/* Prototypes for the parser. These are needed because the parser is mutually
+ recursive. */
+
+static void
+parse_format_string(parser_state *state, formattree *tree);
+
+static void
+parse_format_string_body(parser_state *state, formattree *tree);
+
+static void
+parse_struct(parser_state *state, formattree *tree, Py_ssize_t count);
+
+static void
+parse_character_code(parser_state *state, formattree *tree, Py_ssize_t count);
+
+
+static formattree *
+formattree_new(void)
+{
+ formattree * new_tree = PyMem_MALLOC(sizeof(formattree));
+ if (new_tree == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ new_tree->s_size = 0;
+ new_tree->s_len = 0;
+ new_tree->s_count = 0;
+ new_tree->s_offset = 0;
+ new_tree->s_alignment = 0;
+ new_tree->fmtdef = NULL;
+ new_tree->child = NULL;
+ new_tree->sibling = NULL;
+
+ return new_tree;
+}
+
+
+static void
+formattree_free_r(formattree *tree)
+{
+ formattree *child = tree->child;
+ formattree *del_child = NULL;
+ while (child != NULL) {
+ if (FormatTree_HasChildren(child)) {
+ formattree_free_r(child);
+ }
+ del_child = child;
+ child = child->sibling;
+ PyMem_FREE(del_child);
+ }
+}
+
+static void
+formattree_free(formattree *tree)
+{
+ formattree_free_r(tree);
+ PyMem_FREE(tree);
+}
+
+
/* Helper for integer format codes: converts an arbitrary Python object to a
PyLongObject if possible, otherwise fails. Caller should decref. */
+/* Helper to get a PyLongObject by hook or by crook. Caller should decref. */
+
static PyObject *
get_pylong(PyObject *v)
{
@@ -1143,155 +1255,288 @@
}
-/* Align a size according to a format code. Return -1 on overflow. */
+/* Align a number to a given multiple. */
static Py_ssize_t
-align(Py_ssize_t size, char c, const formatdef *e)
+align_to(Py_ssize_t number, Py_ssize_t multiple)
{
Py_ssize_t extra;
- if (e->format == c) {
- if (e->alignment && size > 0) {
- extra = (e->alignment - 1) - (size - 1) % (e->alignment);
- if (extra > PY_SSIZE_T_MAX - size)
- return -1;
- size += extra;
- }
+ if (multiple && number > 0) {
+ extra = (multiple - 1) - (number - 1) % multiple;
+ if (extra > PY_SSIZE_T_MAX - number)
+ return -1;
+ number += extra;
}
- return size;
+ return number;
}
+static void
+whitespace(parser_state *state)
+{
+ while ( (*state->fmt != '\0') && isspace(Py_CHARMASK(*state->fmt)))
+ state->fmt++;
+}
-/* calculate the size of a format string */
+static int
+match(parser_state *state, char c)
+{
+ if (*state->fmt == c) {
+ state->fmt++;
+ return 0;
+ } else {
+ return -1;
+ }
+}
static int
-prepare_s(PyStructObject *self)
+is_primitive(char c)
{
- const formatdef *f;
- const formatdef *e;
- formatcode *codes;
+ static char *primitive_codes = "xcbB?hHiIlLqQfdspP";
+ const char *begin;
+ for (begin = primitive_codes; *begin; ++begin) {
+ if (c == *begin)
+ return 1;
+ }
+ return isdigit(c);
pv 2011/02/12 19:31:43 This probably shouldn't check for isdigit (count =
+}
- const char *s;
- const char *fmt;
+static int
+is_byte_order_marker(char c)
+{
+ static char *byte_order_codes = "<>!=@";
pv 2011/02/12 19:31:43 What about the '^' byte order marker? It's introdu
+ const char *begin;
+ for (begin = byte_order_codes; *begin; ++begin) {
+ if (c == *begin)
+ return 1;
+ }
+ return 0;
+}
+
+static void
+parse_error(parser_state *state, const char *error_msg)
+{
+ PyErr_SetString(StructError, error_msg);
+ state->error_cnt += 1;
+}
+
+static void
+parse_add_child(parser_state *state, formattree *tree, formattree *child)
+{
+ FormatTree_AppendChild(tree, child);
+ /* Padding does not contribute to the length. */
+ tree->s_len += child->s_count * !FormatTree_IsPadding(child);
+ tree->s_alignment = MAX(tree->s_alignment, child->s_alignment);
+}
+
+static Py_ssize_t
+parse_count(parser_state *state)
+{
char c;
- Py_ssize_t size, len, num, itemsize;
+ Py_ssize_t num;
- fmt = PyBytes_AS_STRING(self->s_format);
+ c = *state->fmt;
+ num = 0;
+ while (isdigit(c)) {
+ /* overflow-safe version of
+ if (num*10 + (c - '0') > PY_SSIZE_T_MAX) { ... } */
+ if (num >= PY_SSIZE_T_MAX / 10 && (
+ num > PY_SSIZE_T_MAX / 10 ||
+ (c - '0') > PY_SSIZE_T_MAX % 10))
+ goto overflow;
+ num = num * 10 + (c - '0');
+ c = *++state->fmt;
+ }
- f = whichtable((char **)&fmt);
+ return num;
+overflow:
+ parse_error(state, "total struct size too long");
+ return -1;
+}
- s = fmt;
- size = 0;
- len = 0;
- while ((c = *s++) != '\0') {
- if (isspace(Py_CHARMASK(c)))
- continue;
- if ('0' <= c && c <= '9') {
- num = c - '0';
- while ('0' <= (c = *s++) && c <= '9') {
- /* overflow-safe version of
- if (num*10 + (c - '0') > PY_SSIZE_T_MAX) { ... } */
- if (num >= PY_SSIZE_T_MAX / 10 && (
- num > PY_SSIZE_T_MAX / 10 ||
- (c - '0') > PY_SSIZE_T_MAX % 10))
- goto overflow;
- num = num*10 + (c - '0');
+static void
+parse_character_code(parser_state *state, formattree *tree, Py_ssize_t count)
+{
+ const char c = *state->fmt++;
+ formattree *new_tree;
+ const formatdef *e;
+
+ assert(is_primitive(c));
+
+ e = getentry(c, state->byte_fmt);
+ if (e != NULL) {
+ new_tree = formattree_new();
+ if (new_tree != NULL) {
+ new_tree->fmtdef = e;
+ if (c == 'p' || c == 's') {
+ new_tree->s_count = 1;
+ new_tree->s_size = count;
+ new_tree->s_alignment = e->alignment;
+ new_tree->s_len = 1;
+ } else if (c == 'x') {
+ /* NOTE: Padding nodes are kept so that we can compute
+ offsets later. We remove these nodes from the
+ tree after computing the offsets. */
+ new_tree->s_count = count;
+ new_tree->s_size = e->size;
+ new_tree->s_alignment = 0;
+ new_tree->s_len = 0;
+ } else {
+ new_tree->s_count = count;
+ new_tree->s_size = e->size;
+ new_tree->s_alignment = e->alignment;
+ new_tree->s_len = count;
}
- if (c == '\0') {
- PyErr_SetString(StructError,
- "repeat count given without format specifier");
- return -1;
- }
+ parse_add_child(state, tree, new_tree);
}
- else
- num = 1;
+ }
+}
- e = getentry(c, f);
- if (e == NULL)
- return -1;
+/* XXX: Limit nested struct depth. */
- switch (c) {
- case 's': /* fall through */
- case 'p': len++; break;
- case 'x': break;
- default: len += num; break;
- }
+static void
+parse_struct(parser_state *state, formattree *tree, Py_ssize_t count)
+{
+ formattree *new_tree;
- itemsize = e->size;
- size = align(size, c, e);
- if (size == -1)
- goto overflow;
+ if (match(state, 'T') == 0) {
+ whitespace(state);
- /* if (size + num * itemsize > PY_SSIZE_T_MAX) { ... } */
- if (num > (PY_SSIZE_T_MAX - size) / itemsize)
- goto overflow;
- size += num * itemsize;
- }
+ if (match(state, '{') == 0) {
+ state->struct_depth++;
- /* check for overflow */
- if ((len + 1) > (PY_SSIZE_T_MAX / sizeof(formatcode))) {
- PyErr_NoMemory();
- return -1;
- }
+ if (state->struct_depth > MAX_STRUCT_DEPTH) {
+ parse_error(state,
+ "the maximum struct nesting depth has been exceeded");
+ return;
+ }
- self->s_size = size;
- self->s_len = len;
- codes = PyMem_MALLOC((len + 1) * sizeof(formatcode));
- if (codes == NULL) {
- PyErr_NoMemory();
- return -1;
+ new_tree = formattree_new();
+
+ if (new_tree != NULL) {
+ whitespace(state);
+
+ if (*state->fmt != '}')
+ parse_format_string_body(state, new_tree);
+
+ if (!state->error_cnt) {
+ new_tree->s_count = count;
+ parse_add_child(state, tree, new_tree);
+
+ whitespace(state);
+ if (match(state, '}') != 0) {
+ parse_error(state,
+ "missing '}' in struct string");
+ }
+ } else {
+ formattree_free(new_tree);
+ }
+ }
+
+ state->struct_depth--;
+ } else {
+ parse_error(state,
+ "missing '{' after 'T' in struct string");
+ }
}
- /* Free any s_codes value left over from a previous initialization. */
- if (self->s_codes != NULL)
- PyMem_FREE(self->s_codes);
- self->s_codes = codes;
+}
- s = fmt;
- size = 0;
- while ((c = *s++) != '\0') {
- if (isspace(Py_CHARMASK(c)))
- continue;
- if ('0' <= c && c <= '9') {
- num = c - '0';
- while ('0' <= (c = *s++) && c <= '9')
- num = num*10 + (c - '0');
- if (c == '\0')
+static void
+parse_format_string_body(parser_state *state, formattree *tree)
+{
+ whitespace(state);
+
+ while (*state->fmt == 'T' || is_primitive(*state->fmt)) {
pv 2011/02/12 19:31:43 ... || isdigit(*state->fmt)
+ Py_ssize_t count = 1;
+
+ if (isdigit(*state->fmt)) {
+ count = parse_count(state);
+ if (count == -1)
break;
}
+
+ if (*state->fmt == 'T')
+ parse_struct(state, tree, count);
+ else if (is_primitive(*state->fmt))
+ parse_character_code(state, tree, count);
else
- num = 1;
+ parse_error(state, "unexpected character in struct string");
- e = getentry(c, f);
+ if (state->error_cnt > 0)
+ break;
- size = align(size, c, e);
- if (c == 's' || c == 'p') {
- codes->offset = size;
- codes->size = num;
- codes->fmtdef = e;
- codes++;
- size += num;
- } else if (c == 'x') {
- size += num;
+ whitespace(state);
+ }
+}
+
+static void
+parse_format_string(parser_state *state, formattree *root)
+{
+ whitespace(state);
+
+ if (is_byte_order_marker(*state->fmt)) {
+ state->byte_fmt = whichtable((char**)&state->fmt);
+ }
+
+ parse_format_string_body(state, root);
+ if (*state->fmt != '\0' && !state->error_cnt)
+ parse_error(state, "unexpected character in struct string");
+}
+
+static int
+compute_offsets(formattree *tree, Py_ssize_t *offset)
+{
+ formattree *child = tree->child;
+
+ while (child != NULL) {
+ formattree *next_child = child->sibling;
+ *offset = child->s_offset = align_to(*offset, child->s_alignment);
+ Py_ssize_t count;
+
+ if (FormatTree_HasChildren(child)) {
+ count = child->s_count - 1;
+ if (compute_offsets(child, offset) < 0)
+ return -1;
} else {
- while (--num >= 0) {
- codes->offset = size;
- codes->size = e->size;
- codes->fmtdef = e;
- codes++;
- size += e->size;
- }
+ count = child->s_count;
}
+
+ if (count && (child->s_size > (PY_SSIZE_T_MAX - *offset) / count)) {
+ PyErr_SetString(StructError, "total struct size too long");
+ return -1;
+ }
+ *offset += count * child->s_size;
+
+ if (FormatTree_IsPadding(child)) {
+ FormatTree_RemoveChild(tree, child);
+ PyMem_FREE(child);
+ }
+ child = next_child;
}
- codes->fmtdef = NULL;
- codes->offset = size;
- codes->size = 0;
+ tree->s_size = *offset - tree->s_offset;
+
return 0;
+}
- overflow:
- PyErr_SetString(StructError,
- "total struct size too long");
- return -1;
+static int
+prepare_s(PyStructObject *self)
+{
+ parser_state state = {
+ native_table,
+ PyBytes_AS_STRING(self->s_format),
+ 0,
+ 0,
+ };
+ Py_ssize_t offset = 0;
+
+ parse_format_string(&state, self->s_tree);
+ if (state.error_cnt == 0) {
+ self->s_tree->s_offset = offset;
+ return compute_offsets(self->s_tree, &offset);
+ } else {
+ return -1;
+ }
}
static PyObject *
@@ -1306,9 +1551,7 @@
PyStructObject *s = (PyStructObject*)self;
Py_INCREF(Py_None);
s->s_format = Py_None;
- s->s_codes = NULL;
- s->s_size = -1;
- s->s_len = -1;
+ s->s_tree = formattree_new();
}
return self;
}
@@ -1357,39 +1600,50 @@
{
if (s->weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *)s);
- if (s->s_codes != NULL) {
- PyMem_FREE(s->s_codes);
+ if (s->s_tree != NULL) {
+ formattree_free(s->s_tree);
}
Py_XDECREF(s->s_format);
Py_TYPE(s)->tp_free((PyObject *)s);
}
+/* Unpacks the given buffer in accordance with the given formattree. The
+ * formattree should not have any children and should have a single format
+ * code defined.
+ *
+ * A Tuple object is returned upon success. NULL is returned upon failure.
+ */
+
static PyObject *
-s_unpack_internal(PyStructObject *soself, char *startfrom) {
- formatcode *code;
+s_unpack_primitive(formattree *tree, const char *buf)
+{
Py_ssize_t i = 0;
- PyObject *result = PyTuple_New(soself->s_len);
+ PyObject *result;
+ PyObject *v;
+ const formatdef *e;
+ const char *res;
+
+ assert(!FormatTree_HasChildren(tree));
+
+ result = PyTuple_New(1);
if (result == NULL)
return NULL;
- for (code = soself->s_codes; code->fmtdef != NULL; code++) {
- PyObject *v;
- const formatdef *e = code->fmtdef;
- const char *res = startfrom + code->offset;
- if (e->format == 's') {
- v = PyBytes_FromStringAndSize(res, code->size);
- } else if (e->format == 'p') {
- Py_ssize_t n = *(unsigned char*)res;
- if (n >= code->size)
- n = code->size - 1;
- v = PyBytes_FromStringAndSize(res + 1, n);
- } else {
- v = e->unpack(res, e);
- }
- if (v == NULL)
- goto fail;
- PyTuple_SET_ITEM(result, i++, v);
+ e = tree->fmtdef;
+ res = buf + tree->s_offset;
+ if (e->format == 's') {
+ v = PyBytes_FromStringAndSize(res, tree->s_size);
+ } else if (e->format == 'p') {
+ Py_ssize_t n = *(unsigned char*)res;
+ if (n >= tree->s_size)
+ n = tree->s_size - 1;
+ v = PyBytes_FromStringAndSize(res + 1, n);
+ } else {
+ v = e->unpack(res, e);
}
+ if (v == NULL)
+ goto fail;
+ PyTuple_SET_ITEM(result, i++, v);
return result;
fail:
@@ -1397,7 +1651,71 @@
return NULL;
}
+/* Unpacks the given buffer in accordance with the given formattree. The
+ * formattree specifies how the given character buffer should be unpacked.
+ *
+ * A Tuple object is returned upon success. This Tuple object may have nested
+ * Tuple objects, if the formattree dictates as such. NULL is returned upon
+ * failure.
+ */
+static PyObject *
+s_unpack_struct(formattree *tree, const char *buf)
+{
+ formattree *child;
+ PyObject *tup = PyTuple_New(0);
minge 2011/01/07 03:59:45 Since we know the number of arguments that we are
+ if (tup == NULL)
+ return NULL;
+
+ for (child = tree->child; child != NULL; child = child->sibling) {
+ PyObject *sub_tup = NULL;
+ PyObject *new_tup = NULL;
+ const char *child_buf;
+ Py_ssize_t count;
+
+ for (count = 0; count < child->s_count; ++count) {
+ child_buf = buf + count * child->s_size;
+
+ if (FormatTree_HasChildren(child)) {
+ PyObject *wrap_tup = NULL;
+ sub_tup = s_unpack_struct(child, child_buf);
+ if (sub_tup == NULL)
+ goto fail;
+ wrap_tup = PyTuple_New(1);
+ if (wrap_tup == NULL) {
+ Py_DECREF(sub_tup);
+ goto fail;
+ }
+ PyTuple_SetItem(wrap_tup, 0, sub_tup);
+ new_tup = PySequence_Concat(tup, wrap_tup);
+ Py_DECREF(wrap_tup);
+ } else {
+ sub_tup = s_unpack_primitive(child, child_buf);
+ if (sub_tup == NULL)
+ goto fail;
+ new_tup = PySequence_Concat(tup, sub_tup);
+ Py_DECREF(sub_tup);
+ }
+
+ Py_DECREF(tup);
+ if (new_tup == NULL)
+ goto fail;
+ tup = new_tup;
+ }
+ }
+
+ return tup;
+fail:
+ Py_DECREF(tup);
+ return NULL;
+}
+
+static PyObject *
+s_unpack_internal(PyStructObject *soself, char *startfrom)
+{
+ return s_unpack_struct(soself->s_tree, startfrom);
+}
+
PyDoc_STRVAR(s_unpack__doc__,
"S.unpack(buffer) -> (v1, v2, ...)\n\
\n\
@@ -1413,13 +1731,13 @@
PyStructObject *soself = (PyStructObject *)self;
assert(PyStruct_Check(self));
- assert(soself->s_codes != NULL);
+ assert(soself->s_tree != NULL);
if (PyObject_GetBuffer(input, &vbuf, PyBUF_SIMPLE) < 0)
return NULL;
- if (vbuf.len != soself->s_size) {
+ if (vbuf.len != soself->s_tree->s_size) {
PyErr_Format(StructError,
"unpack requires a bytes object of length %zd",
- soself->s_size);
+ soself->s_tree->s_size);
PyBuffer_Release(&vbuf);
return NULL;
}
@@ -1447,7 +1765,7 @@
PyStructObject *soself = (PyStructObject *)self;
assert(PyStruct_Check(self));
- assert(soself->s_codes != NULL);
+ assert(soself->s_tree != NULL);
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"O|n:unpack_from", kwlist,
@@ -1457,10 +1775,10 @@
return NULL;
if (offset < 0)
offset += vbuf.len;
- if (offset < 0 || vbuf.len - offset < soself->s_size) {
+ if (offset < 0 || vbuf.len - offset < soself->s_tree->s_size) {
PyErr_Format(StructError,
"unpack_from requires a buffer of at least %zd bytes",
- soself->s_size);
+ soself->s_tree->s_size);
PyBuffer_Release(&vbuf);
return NULL;
}
@@ -1469,91 +1787,141 @@
return result;
}
+/* Packs a non-nested tuples of arguments to the given buffer.
+ *
+ * Takes a struct object, a tuple of arguments, and a character buffer for
+ * writing the packed string. 0 is returned on success, -1 is returned if
+ * there is an error.
+ */
-/*
- * Guts of the pack function.
+static int
+s_pack_primitive(formattree *tree, PyObject *v, char *buf)
+{
+ Py_ssize_t n;
+ const formatdef *e;
+ char *res;
+
+ e = tree->fmtdef;
+ res = buf + tree->s_offset;
+ if (e->format == 's') {
+ int isstring;
+ void *p;
+ isstring = PyBytes_Check(v);
+ if (!isstring && !PyByteArray_Check(v)) {
+ PyErr_SetString(StructError,
+ "argument for 's' must be a bytes or string");
+ return -1;
+ }
+ if (isstring) {
+ n = PyBytes_GET_SIZE(v);
+ p = PyBytes_AS_STRING(v);
+ }
+ else {
+ n = PyByteArray_GET_SIZE(v);
+ p = PyByteArray_AS_STRING(v);
+ }
+ if (n > tree->s_size)
+ n = tree->s_size;
pv 2011/02/12 19:31:43 Should too long an input string be an error?
+ if (n > 0)
+ memcpy(res, p, n);
+ } else if (e->format == 'p') {
+ int isstring;
+ void *p;
+ isstring = PyBytes_Check(v);
+ if (!isstring && !PyByteArray_Check(v)) {
+ PyErr_SetString(StructError,
+ "argument for 'p' must be a bytes or string");
+ return -1;
+ }
+ if (isstring) {
+ n = PyBytes_GET_SIZE(v);
+ p = PyBytes_AS_STRING(v);
+ }
+ else {
+ n = PyByteArray_GET_SIZE(v);
+ p = PyByteArray_AS_STRING(v);
+ }
+ if (n > (tree->s_size - 1))
+ n = tree->s_size - 1;
+ if (n > 0)
+ memcpy(res + 1, p, n);
+ if (n > 255)
+ n = 255;
+ *res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char);
+ } else {
+ if (e->pack(res, v, e) < 0) {
+ if (PyLong_Check(v) && PyErr_ExceptionMatches(PyExc_OverflowError))
+ PyErr_SetString(StructError,
+ "long too large to convert to int");
+ return -1;
+ }
+ }
+
+ /* Success */
+ return 0;
+}
+
+
+/* Packs a potentially nested tuple of arguments to the given buffer.
*
- * Takes a struct object, a tuple of arguments, and offset in that tuple of
- * argument for where to start processing the arguments for packing, and a
- * character buffer for writing the packed string. The caller must insure
- * that the buffer may contain the required length for packing the arguments.
- * 0 is returned on success, 1 is returned if there is an error.
- *
+ * Takes a struct object, a tuple of arguments, and a character buffer for
+ * writing the packed string. If there are nested structures within the given
+ * structure, then the nested structures are recursively packed. 0 is returned
+ * on success, -1 is returned if there is an error.
*/
+
static int
-s_pack_internal(PyStructObject *soself, PyObject *args, int offset, char* buf)
+s_pack_struct(formattree *tree, PyObject *args, char *buf)
{
- formatcode *code;
- /* XXX(nnorwitz): why does i need to be a local? can we use
- the offset parameter or do we need the wider width? */
- Py_ssize_t i;
+ Py_ssize_t arg_i = 0;
+ formattree *child;
- memset(buf, '\0', soself->s_size);
- i = offset;
- for (code = soself->s_codes; code->fmtdef != NULL; code++) {
- Py_ssize_t n;
- PyObject *v = PyTuple_GET_ITEM(args, i++);
- const formatdef *e = code->fmtdef;
- char *res = buf + code->offset;
- if (e->format == 's') {
- int isstring;
- void *p;
- isstring = PyBytes_Check(v);
- if (!isstring && !PyByteArray_Check(v)) {
- PyErr_SetString(StructError,
- "argument for 's' must be a bytes object");
- return -1;
+ if (PyTuple_GET_SIZE(args) != tree->s_len)
+ {
+ PyErr_Format(StructError,
+ "pack requires exactly %zd arguments", tree->s_len);
+ return -1;
+ }
+
+ for (child = tree->child; child != NULL; child = child->sibling) {
+ int ret = 0;
+ Py_ssize_t count;
+
+ for (count = 0; count < child->s_count; ++count) {
+ PyObject *arg = PyTuple_GET_ITEM(args, arg_i);
+ char *child_buf = buf + count * child->s_size;
+
+ if (FormatTree_HasChildren(child)) {
+ if (!PyTuple_Check(arg)) {
+ PyErr_SetString(StructError,
+ "argument for 'T{...}' must be a tuple");
+ return -1;
+ }
+ ret = s_pack_struct(child, arg, child_buf);
+ } else {
+ ret = s_pack_primitive(child, arg, child_buf);
}
- if (isstring) {
- n = PyBytes_GET_SIZE(v);
- p = PyBytes_AS_STRING(v);
- }
- else {
- n = PyByteArray_GET_SIZE(v);
- p = PyByteArray_AS_STRING(v);
- }
- if (n > code->size)
- n = code->size;
- if (n > 0)
- memcpy(res, p, n);
- } else if (e->format == 'p') {
- int isstring;
- void *p;
- isstring = PyBytes_Check(v);
- if (!isstring && !PyByteArray_Check(v)) {
- PyErr_SetString(StructError,
- "argument for 'p' must be a bytes object");
+
+ if (ret < 0) {
return -1;
}
- if (isstring) {
- n = PyBytes_GET_SIZE(v);
- p = PyBytes_AS_STRING(v);
- }
- else {
- n = PyByteArray_GET_SIZE(v);
- p = PyByteArray_AS_STRING(v);
- }
- if (n > (code->size - 1))
- n = code->size - 1;
- if (n > 0)
- memcpy(res + 1, p, n);
- if (n > 255)
- n = 255;
- *res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char);
- } else {
- if (e->pack(res, v, e) < 0) {
- if (PyLong_Check(v) && PyErr_ExceptionMatches(PyExc_OverflowError))
- PyErr_SetString(StructError,
- "long too large to convert to int");
- return -1;
- }
+
+ arg_i += 1;
}
}
- /* Success */
return 0;
}
+static int
+s_pack_internal(PyStructObject *soself, PyObject *args, int offset, char *buf)
+{
+ memset(buf, '\0', soself->s_tree->s_size);
+ PyObject *new_args = PyTuple_GetSlice(args, offset, PyTuple_Size(args));
+ int ret = s_pack_struct(soself->s_tree, new_args, buf);
+ Py_DECREF(new_args);
+ return ret;
+}
PyDoc_STRVAR(s_pack__doc__,
"S.pack(v1, v2, ...) -> bytes\n\
@@ -1571,16 +1939,9 @@
/* Validate arguments. */
soself = (PyStructObject *)self;
assert(PyStruct_Check(self));
- assert(soself->s_codes != NULL);
- if (PyTuple_GET_SIZE(args) != soself->s_len)
- {
- PyErr_Format(StructError,
- "pack requires exactly %zd arguments", soself->s_len);
- return NULL;
- }
/* Allocate a new string */
- result = PyBytes_FromStringAndSize((char *)NULL, soself->s_size);
+ result = PyBytes_FromStringAndSize((char *)NULL, soself->s_tree->s_size);
if (result == NULL)
return NULL;
@@ -1611,14 +1972,6 @@
/* Validate arguments. +1 is for the first arg as buffer. */
soself = (PyStructObject *)self;
assert(PyStruct_Check(self));
- assert(soself->s_codes != NULL);
- if (PyTuple_GET_SIZE(args) != (soself->s_len + 2))
- {
- PyErr_Format(StructError,
- "pack_into requires exactly %zd arguments",
- (soself->s_len + 2));
- return NULL;
- }
/* Extract a writable memory buffer from the first argument */
if ( PyObject_AsWriteBuffer(PyTuple_GET_ITEM(args, 0),
@@ -1637,10 +1990,10 @@
offset += buffer_len;
/* Check boundaries */
- if (offset < 0 || (buffer_len - offset) < soself->s_size) {
+ if (offset < 0 || (buffer_len - offset) < soself->s_tree->s_size) {
PyErr_Format(StructError,
"pack_into requires a buffer of at least %zd bytes",
- soself->s_size);
+ soself->s_tree->s_size);
return NULL;
}
@@ -1662,7 +2015,7 @@
static PyObject *
s_get_size(PyStructObject *self, void *unused)
{
- return PyLong_FromSsize_t(self->s_size);
+ return PyLong_FromSsize_t(self->s_tree->s_size);
}
/* List of functions */
@@ -1789,7 +2142,7 @@
PyObject *s_object = cache_struct(fmt);
if (s_object == NULL)
return NULL;
- n = ((PyStructObject *)s_object)->s_size;
+ n = ((PyStructObject *)s_object)->s_tree->s_size;
Py_DECREF(s_object);
return PyLong_FromSsize_t(n);
}
@@ -1943,8 +2296,9 @@
>: 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\
+The primitive format chars are used to indicate types of primitive arg\n\
+values and must match exactly; these can be preceded by a decimal repeat\n\
+count:\n\
x: pad byte (no data); c:char; b:signed byte; B:unsigned byte;\n\
?: _Bool (requires C99; if not available, char is used instead)\n\
h:short; H:unsigned short; i:int; I:unsigned int;\n\
@@ -1957,6 +2311,10 @@
q:long long; Q:unsigned long long\n\
Whitespace between formats is ignored.\n\
\n\
+Additionally, there may be nested structures. These are specified by\n\
+surrounding a format string with 'T{' ... '}'. They maybe arbitrarily\n\
+nested.\n\
+\n\
The variable struct.error is an exception raised on errors.\n");
« Lib/test/test_struct.py ('K') | « Lib/test/test_struct.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b