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"); |