diff options
Diffstat (limited to 'Modules/arraymodule.c')
-rw-r--r-- | Modules/arraymodule.c | 1260 |
1 files changed, 894 insertions, 366 deletions
diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 5a92862a7c3..475acb46920 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -22,10 +22,13 @@ struct arrayobject; /* Forward */ * functions aren't visible yet. */ struct arraydescr { - int typecode; + Py_UNICODE typecode; int itemsize; PyObject * (*getitem)(struct arrayobject *, Py_ssize_t); int (*setitem)(struct arrayobject *, Py_ssize_t, PyObject *); + char *formats; + int is_integer_type; + int is_signed; }; typedef struct arrayobject { @@ -34,6 +37,7 @@ typedef struct arrayobject { Py_ssize_t allocated; struct arraydescr *ob_descr; PyObject *weakreflist; /* List of weak references */ + int ob_exports; /* Number of exported buffers */ } arrayobject; static PyTypeObject Arraytype; @@ -47,9 +51,15 @@ array_resize(arrayobject *self, Py_ssize_t newsize) char *items; size_t _new_size; + if (self->ob_exports > 0 && newsize != Py_SIZE(self)) { + PyErr_SetString(PyExc_BufferError, + "cannot resize an array that is exporting buffers"); + return -1; + } + /* Bypass realloc() when a previous overallocation is large enough to accommodate the newsize. If the newsize is 16 smaller than the - current size, then proceed with the realloc() to shrink the list. + current size, then proceed with the realloc() to shrink the array. */ if (self->allocated >= newsize && @@ -59,6 +69,14 @@ array_resize(arrayobject *self, Py_ssize_t newsize) return 0; } + if (newsize == 0) { + PyMem_FREE(self->ob_item); + self->ob_item = NULL; + Py_SIZE(self) = 0; + self->allocated = 0; + return 0; + } + /* This over-allocates proportional to the array size, making room * for additional growth. The over-allocation is mild, but is * enough to give linear-time amortized behavior over a long @@ -102,29 +120,12 @@ in bounds; that's the responsibility of the caller. ****************************************************************************/ static PyObject * -c_getitem(arrayobject *ap, Py_ssize_t i) -{ - return PyString_FromStringAndSize(&((char *)ap->ob_item)[i], 1); -} - -static int -c_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) -{ - char x; - if (!PyArg_Parse(v, "c;array item must be char", &x)) - return -1; - if (i >= 0) - ((char *)ap->ob_item)[i] = x; - return 0; -} - -static PyObject * b_getitem(arrayobject *ap, Py_ssize_t i) { long x = ((char *)ap->ob_item)[i]; if (x >= 128) x -= 256; - return PyInt_FromLong(x); + return PyLong_FromLong(x); } static int @@ -155,7 +156,7 @@ static PyObject * BB_getitem(arrayobject *ap, Py_ssize_t i) { long x = ((unsigned char *)ap->ob_item)[i]; - return PyInt_FromLong(x); + return PyLong_FromLong(x); } static int @@ -170,7 +171,6 @@ BB_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) return 0; } -#ifdef Py_USING_UNICODE static PyObject * u_getitem(arrayobject *ap, Py_ssize_t i) { @@ -194,14 +194,15 @@ u_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) ((Py_UNICODE *)ap->ob_item)[i] = p[0]; return 0; } -#endif + static PyObject * h_getitem(arrayobject *ap, Py_ssize_t i) { - return PyInt_FromLong((long) ((short *)ap->ob_item)[i]); + return PyLong_FromLong((long) ((short *)ap->ob_item)[i]); } + static int h_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) { @@ -217,7 +218,7 @@ h_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) static PyObject * HH_getitem(arrayobject *ap, Py_ssize_t i) { - return PyInt_FromLong((long) ((unsigned short *)ap->ob_item)[i]); + return PyLong_FromLong((long) ((unsigned short *)ap->ob_item)[i]); } static int @@ -246,7 +247,7 @@ HH_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) static PyObject * i_getitem(arrayobject *ap, Py_ssize_t i) { - return PyInt_FromLong((long) ((int *)ap->ob_item)[i]); + return PyLong_FromLong((long) ((int *)ap->ob_item)[i]); } static int @@ -303,7 +304,7 @@ II_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) static PyObject * l_getitem(arrayobject *ap, Py_ssize_t i) { - return PyInt_FromLong(((long *)ap->ob_item)[i]); + return PyLong_FromLong(((long *)ap->ob_item)[i]); } static int @@ -389,23 +390,25 @@ d_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) return 0; } -/* Description of types */ + +/* Description of types. + * + * Don't forget to update typecode_to_mformat_code() if you add a new + * typecode. + */ static struct arraydescr descriptors[] = { - {'c', sizeof(char), c_getitem, c_setitem}, - {'b', sizeof(char), b_getitem, b_setitem}, - {'B', sizeof(char), BB_getitem, BB_setitem}, -#ifdef Py_USING_UNICODE - {'u', sizeof(Py_UNICODE), u_getitem, u_setitem}, -#endif - {'h', sizeof(short), h_getitem, h_setitem}, - {'H', sizeof(short), HH_getitem, HH_setitem}, - {'i', sizeof(int), i_getitem, i_setitem}, - {'I', sizeof(int), II_getitem, II_setitem}, - {'l', sizeof(long), l_getitem, l_setitem}, - {'L', sizeof(long), LL_getitem, LL_setitem}, - {'f', sizeof(float), f_getitem, f_setitem}, - {'d', sizeof(double), d_getitem, d_setitem}, - {'\0', 0, 0, 0} /* Sentinel */ + {'b', 1, b_getitem, b_setitem, "b", 1, 1}, + {'B', 1, BB_getitem, BB_setitem, "B", 1, 0}, + {'u', sizeof(Py_UNICODE), u_getitem, u_setitem, "u", 0, 0}, + {'h', sizeof(short), h_getitem, h_setitem, "h", 1, 1}, + {'H', sizeof(short), HH_getitem, HH_setitem, "H", 1, 0}, + {'i', sizeof(int), i_getitem, i_setitem, "i", 1, 1}, + {'I', sizeof(int), II_getitem, II_setitem, "I", 1, 0}, + {'l', sizeof(long), l_getitem, l_setitem, "l", 1, 1}, + {'L', sizeof(long), LL_getitem, LL_setitem, "L", 1, 0}, + {'f', sizeof(float), f_getitem, f_setitem, "f", 0, 0}, + {'d', sizeof(double), d_getitem, d_setitem, "d", 0, 0}, + {'\0', 0, 0, 0, 0, 0, 0} /* Sentinel */ }; /**************************************************************************** @@ -446,6 +449,7 @@ newarrayobject(PyTypeObject *type, Py_ssize_t size, struct arraydescr *descr) return PyErr_NoMemory(); } } + op->ob_exports = 0; return (PyObject *) op; } @@ -670,11 +674,9 @@ array_concat(arrayobject *a, PyObject *bb) static PyObject * array_repeat(arrayobject *a, Py_ssize_t n) { - Py_ssize_t i; Py_ssize_t size; arrayobject *np; - char *p; - Py_ssize_t nbytes; + Py_ssize_t oldbytes, newbytes; if (n < 0) n = 0; if ((Py_SIZE(a) != 0) && (n > PY_SSIZE_T_MAX / Py_SIZE(a))) { @@ -684,13 +686,23 @@ array_repeat(arrayobject *a, Py_ssize_t n) np = (arrayobject *) newarrayobject(&Arraytype, size, a->ob_descr); if (np == NULL) return NULL; - p = np->ob_item; - nbytes = Py_SIZE(a) * a->ob_descr->itemsize; - for (i = 0; i < n; i++) { - memcpy(p, a->ob_item, nbytes); - p += nbytes; + if (n == 0) + return (PyObject *)np; + oldbytes = Py_SIZE(a) * a->ob_descr->itemsize; + newbytes = oldbytes * n; + /* this follows the code in unicode_repeat */ + if (oldbytes == 1) { + memset(np->ob_item, a->ob_item[0], newbytes); + } else { + Py_ssize_t done = oldbytes; + Py_MEMCPY(np->ob_item, a->ob_item, oldbytes); + while (done < newbytes) { + Py_ssize_t ncopy = (done <= newbytes-done) ? done : newbytes-done; + Py_MEMCPY(np->ob_item+done, np->ob_item, ncopy); + done += ncopy; + } } - return (PyObject *) np; + return (PyObject *)np; } static int @@ -737,29 +749,27 @@ array_ass_slice(arrayobject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v) ihigh = Py_SIZE(a); item = a->ob_item; d = n - (ihigh-ilow); + /* Issue #4509: If the array has exported buffers and the slice + assignment would change the size of the array, fail early to make + sure we don't modify it. */ + if (d != 0 && a->ob_exports > 0) { + PyErr_SetString(PyExc_BufferError, + "cannot resize an array that is exporting buffers"); + return -1; + } if (d < 0) { /* Delete -d items */ memmove(item + (ihigh+d)*a->ob_descr->itemsize, item + ihigh*a->ob_descr->itemsize, (Py_SIZE(a)-ihigh)*a->ob_descr->itemsize); - Py_SIZE(a) += d; - PyMem_RESIZE(item, char, Py_SIZE(a)*a->ob_descr->itemsize); - /* Can't fail */ - a->ob_item = item; - a->allocated = Py_SIZE(a); + if (array_resize(a, Py_SIZE(a) + d) == -1) + return -1; } else if (d > 0) { /* Insert d items */ - PyMem_RESIZE(item, char, - (Py_SIZE(a) + d)*a->ob_descr->itemsize); - if (item == NULL) { - PyErr_NoMemory(); + if (array_resize(a, Py_SIZE(a) + d)) return -1; - } memmove(item + (ihigh+d)*a->ob_descr->itemsize, item + ihigh*a->ob_descr->itemsize, (Py_SIZE(a)-ihigh)*a->ob_descr->itemsize); - a->ob_item = item; - Py_SIZE(a) += d; - a->allocated = Py_SIZE(a); } if (n > 0) memcpy(item + ilow*a->ob_descr->itemsize, b->ob_item, @@ -814,8 +824,7 @@ array_iter_extend(arrayobject *self, PyObject *bb) static int array_do_extend(arrayobject *self, PyObject *bb) { - Py_ssize_t size; - char *old_item; + Py_ssize_t size, oldsize, bbsize; if (!array_Check(bb)) return array_iter_extend(self, bb); @@ -830,18 +839,14 @@ array_do_extend(arrayobject *self, PyObject *bb) PyErr_NoMemory(); return -1; } - size = Py_SIZE(self) + Py_SIZE(b); - old_item = self->ob_item; - PyMem_RESIZE(self->ob_item, char, size*self->ob_descr->itemsize); - if (self->ob_item == NULL) { - self->ob_item = old_item; - PyErr_NoMemory(); + oldsize = Py_SIZE(self); + /* Get the size of bb before resizing the array since bb could be self. */ + bbsize = Py_SIZE(bb); + size = oldsize + Py_SIZE(b); + if (array_resize(self, size) == -1) return -1; - } - memcpy(self->ob_item + Py_SIZE(self)*self->ob_descr->itemsize, - b->ob_item, Py_SIZE(b)*b->ob_descr->itemsize); - Py_SIZE(self) = size; - self->allocated = size; + memcpy(self->ob_item + oldsize * self->ob_descr->itemsize, + b->ob_item, bbsize * b->ob_descr->itemsize); return 0; #undef b @@ -877,27 +882,15 @@ array_inplace_repeat(arrayobject *self, Py_ssize_t n) return PyErr_NoMemory(); } size = Py_SIZE(self) * self->ob_descr->itemsize; - if (n == 0) { - PyMem_FREE(items); - self->ob_item = NULL; - Py_SIZE(self) = 0; - self->allocated = 0; + if (n > 0 && size > PY_SSIZE_T_MAX / n) { + return PyErr_NoMemory(); } - else { - if (size > PY_SSIZE_T_MAX / n) { - return PyErr_NoMemory(); - } - PyMem_RESIZE(items, char, n * size); - if (items == NULL) - return PyErr_NoMemory(); - p = items; - for (i = 1; i < n; i++) { - p += size; - memcpy(p, items, size); - } - self->ob_item = items; - Py_SIZE(self) *= n; - self->allocated = Py_SIZE(self); + if (array_resize(self, n * Py_SIZE(self)) == -1) + return NULL; + items = p = self->ob_item; + for (i = 1; i < n; i++) { + p += size; + memcpy(p, items, size); } } Py_INCREF(self); @@ -929,7 +922,7 @@ array_count(arrayobject *self, PyObject *v) else if (cmp < 0) return NULL; } - return PyInt_FromSsize_t(count); + return PyLong_FromSsize_t(count); } PyDoc_STRVAR(count_doc, @@ -947,7 +940,7 @@ array_index(arrayobject *self, PyObject *v) int cmp = PyObject_RichCompareBool(selfi, v, Py_EQ); Py_DECREF(selfi); if (cmp > 0) { - return PyInt_FromLong((long)i); + return PyLong_FromLong((long)i); } else if (cmp < 0) return NULL; @@ -1073,7 +1066,7 @@ array_buffer_info(arrayobject *self, PyObject *unused) return NULL; PyTuple_SET_ITEM(retval, 0, PyLong_FromVoidPtr(self->ob_item)); - PyTuple_SET_ITEM(retval, 1, PyInt_FromLong((long)(Py_SIZE(self)))); + PyTuple_SET_ITEM(retval, 1, PyLong_FromLong((long)(Py_SIZE(self)))); return retval; } @@ -1188,96 +1181,97 @@ PyDoc_STRVAR(reverse_doc, \n\ Reverse the order of the items in the array."); + +/* Forward */ +static PyObject *array_frombytes(arrayobject *self, PyObject *args); + static PyObject * array_fromfile(arrayobject *self, PyObject *args) { - PyObject *f; - Py_ssize_t n; - FILE *fp; + PyObject *f, *b, *res; + Py_ssize_t itemsize = self->ob_descr->itemsize; + Py_ssize_t n, nbytes; + int not_enough_bytes; + if (!PyArg_ParseTuple(args, "On:fromfile", &f, &n)) return NULL; - fp = PyFile_AsFile(f); - if (fp == NULL) { - PyErr_SetString(PyExc_TypeError, "arg1 must be open file"); + + nbytes = n * itemsize; + if (nbytes < 0 || nbytes/itemsize != n) { + PyErr_NoMemory(); return NULL; } - if (n > 0) { - char *item = self->ob_item; - Py_ssize_t itemsize = self->ob_descr->itemsize; - size_t nread; - Py_ssize_t newlength; - size_t newbytes; - /* Be careful here about overflow */ - if ((newlength = Py_SIZE(self) + n) <= 0 || - (newbytes = newlength * itemsize) / itemsize != - (size_t)newlength) - goto nomem; - PyMem_RESIZE(item, char, newbytes); - if (item == NULL) { - nomem: - PyErr_NoMemory(); - return NULL; - } - self->ob_item = item; - Py_SIZE(self) += n; - self->allocated = Py_SIZE(self); - nread = fread(item + (Py_SIZE(self) - n) * itemsize, - itemsize, n, fp); - if (nread < (size_t)n) { - Py_SIZE(self) -= (n - nread); - PyMem_RESIZE(item, char, Py_SIZE(self)*itemsize); - self->ob_item = item; - self->allocated = Py_SIZE(self); - if (ferror(fp)) { - PyErr_SetFromErrno(PyExc_IOError); - clearerr(fp); - } - else { - PyErr_SetString(PyExc_EOFError, - "not enough items in file"); - } - return NULL; - } + + b = PyObject_CallMethod(f, "read", "n", nbytes); + if (b == NULL) + return NULL; + + if (!PyBytes_Check(b)) { + PyErr_SetString(PyExc_TypeError, + "read() didn't return bytes"); + Py_DECREF(b); + return NULL; } - Py_INCREF(Py_None); - return Py_None; + + not_enough_bytes = (PyBytes_GET_SIZE(b) != nbytes); + + args = Py_BuildValue("(O)", b); + Py_DECREF(b); + if (args == NULL) + return NULL; + + res = array_frombytes(self, args); + Py_DECREF(args); + if (res == NULL) + return NULL; + + if (not_enough_bytes) { + PyErr_SetString(PyExc_EOFError, + "read() didn't return enough bytes"); + Py_DECREF(res); + return NULL; + } + + return res; } PyDoc_STRVAR(fromfile_doc, "fromfile(f, n)\n\ \n\ Read n objects from the file object f and append them to the end of the\n\ -array. Also called as read."); - - -static PyObject * -array_fromfile_as_read(arrayobject *self, PyObject *args) -{ - if (PyErr_WarnPy3k("array.read() not supported in 3.x; " - "use array.fromfile()", 1) < 0) - return NULL; - return array_fromfile(self, args); -} +array."); static PyObject * array_tofile(arrayobject *self, PyObject *f) { - FILE *fp; + Py_ssize_t nbytes = Py_SIZE(self) * self->ob_descr->itemsize; + /* Write 64K blocks at a time */ + /* XXX Make the block size settable */ + int BLOCKSIZE = 64*1024; + Py_ssize_t nblocks = (nbytes + BLOCKSIZE - 1) / BLOCKSIZE; + Py_ssize_t i; - fp = PyFile_AsFile(f); - if (fp == NULL) { - PyErr_SetString(PyExc_TypeError, "arg must be open file"); - return NULL; - } - if (self->ob_size > 0) { - if (fwrite(self->ob_item, self->ob_descr->itemsize, - self->ob_size, fp) != (size_t)self->ob_size) { - PyErr_SetFromErrno(PyExc_IOError); - clearerr(fp); + if (Py_SIZE(self) == 0) + goto done; + + for (i = 0; i < nblocks; i++) { + char* ptr = self->ob_item + i*BLOCKSIZE; + Py_ssize_t size = BLOCKSIZE; + PyObject *bytes, *res; + if (i*BLOCKSIZE + size > nbytes) + size = nbytes - i*BLOCKSIZE; + bytes = PyBytes_FromStringAndSize(ptr, size); + if (bytes == NULL) return NULL; - } + res = PyObject_CallMethod(f, "write", "O", bytes); + Py_DECREF(bytes); + if (res == NULL) + return NULL; + Py_DECREF(res); /* drop write result */ } + + done: Py_INCREF(Py_None); return Py_None; } @@ -1285,25 +1279,13 @@ array_tofile(arrayobject *self, PyObject *f) PyDoc_STRVAR(tofile_doc, "tofile(f)\n\ \n\ -Write all items (as machine values) to the file object f. Also called as\n\ -write."); - - -static PyObject * -array_tofile_as_write(arrayobject *self, PyObject *f) -{ - if (PyErr_WarnPy3k("array.write() not supported in 3.x; " - "use array.tofile()", 1) < 0) - return NULL; - return array_tofile(self, f); -} +Write all items (as machine values) to the file object f."); static PyObject * array_fromlist(arrayobject *self, PyObject *list) { Py_ssize_t n; - Py_ssize_t itemsize = self->ob_descr->itemsize; if (!PyList_Check(list)) { PyErr_SetString(PyExc_TypeError, "arg must be list"); @@ -1311,28 +1293,15 @@ array_fromlist(arrayobject *self, PyObject *list) } n = PyList_Size(list); if (n > 0) { - char *item = self->ob_item; - Py_ssize_t i; - PyMem_RESIZE(item, char, (Py_SIZE(self) + n) * itemsize); - if (item == NULL) { - PyErr_NoMemory(); + Py_ssize_t i, old_size; + old_size = Py_SIZE(self); + if (array_resize(self, old_size + n) == -1) return NULL; - } - self->ob_item = item; - Py_SIZE(self) += n; - self->allocated = Py_SIZE(self); for (i = 0; i < n; i++) { PyObject *v = PyList_GetItem(list, i); if ((*self->ob_descr->setitem)(self, Py_SIZE(self) - n + i, v) != 0) { - Py_SIZE(self) -= n; - if (itemsize && (self->ob_size > PY_SSIZE_T_MAX / itemsize)) { - return PyErr_NoMemory(); - } - PyMem_RESIZE(item, char, - Py_SIZE(self) * itemsize); - self->ob_item = item; - self->allocated = Py_SIZE(self); + array_resize(self, old_size); return NULL; } } @@ -1346,7 +1315,6 @@ PyDoc_STRVAR(fromlist_doc, \n\ Append items to array from list."); - static PyObject * array_tolist(arrayobject *self, PyObject *unused) { @@ -1371,97 +1339,139 @@ PyDoc_STRVAR(tolist_doc, \n\ Convert array to an ordinary list with the same items."); - static PyObject * -array_fromstring(arrayobject *self, PyObject *args) +frombytes(arrayobject *self, Py_buffer *buffer) { - char *str; - Py_ssize_t n; int itemsize = self->ob_descr->itemsize; - if (!PyArg_ParseTuple(args, "s#:fromstring", &str, &n)) + Py_ssize_t n; + if (buffer->itemsize != 1) { + PyBuffer_Release(buffer); + PyErr_SetString(PyExc_TypeError, "string/buffer of bytes required."); return NULL; + } + n = buffer->len; if (n % itemsize != 0) { + PyBuffer_Release(buffer); PyErr_SetString(PyExc_ValueError, "string length not a multiple of item size"); return NULL; } n = n / itemsize; if (n > 0) { - char *item = self->ob_item; - if ((n > PY_SSIZE_T_MAX - Py_SIZE(self)) || - ((Py_SIZE(self) + n) > PY_SSIZE_T_MAX / itemsize)) { + Py_ssize_t old_size = Py_SIZE(self); + if ((n > PY_SSIZE_T_MAX - old_size) || + ((old_size + n) > PY_SSIZE_T_MAX / itemsize)) { + PyBuffer_Release(buffer); return PyErr_NoMemory(); } - PyMem_RESIZE(item, char, (Py_SIZE(self) + n) * itemsize); - if (item == NULL) { - PyErr_NoMemory(); + if (array_resize(self, old_size + n) == -1) { + PyBuffer_Release(buffer); return NULL; } - self->ob_item = item; - Py_SIZE(self) += n; - self->allocated = Py_SIZE(self); - memcpy(item + (Py_SIZE(self) - n) * itemsize, - str, itemsize*n); + memcpy(self->ob_item + old_size * itemsize, + buffer->buf, n * itemsize); } + PyBuffer_Release(buffer); Py_INCREF(Py_None); return Py_None; } +static PyObject * +array_fromstring(arrayobject *self, PyObject *args) +{ + Py_buffer buffer; + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "fromstring() is deprecated. Use frombytes() instead.", 2) != 0) + return NULL; + if (!PyArg_ParseTuple(args, "s*:fromstring", &buffer)) + return NULL; + else + return frombytes(self, &buffer); +} + PyDoc_STRVAR(fromstring_doc, "fromstring(string)\n\ \n\ Appends items from the string, interpreting it as an array of machine\n\ -values,as if it had been read from a file using the fromfile() method)."); +values, as if it had been read from a file using the fromfile() method).\n\ +\n\ +This method is deprecated. Use frombytes instead."); static PyObject * -array_tostring(arrayobject *self, PyObject *unused) +array_frombytes(arrayobject *self, PyObject *args) { - if (self->ob_size <= PY_SSIZE_T_MAX / self->ob_descr->itemsize) { - return PyString_FromStringAndSize(self->ob_item, + Py_buffer buffer; + if (!PyArg_ParseTuple(args, "y*:frombytes", &buffer)) + return NULL; + else + return frombytes(self, &buffer); +} + +PyDoc_STRVAR(frombytes_doc, +"frombytes(bytestring)\n\ +\n\ +Appends items from the string, interpreting it as an array of machine\n\ +values, as if it had been read from a file using the fromfile() method)."); + + +static PyObject * +array_tobytes(arrayobject *self, PyObject *unused) +{ + if (Py_SIZE(self) <= PY_SSIZE_T_MAX / self->ob_descr->itemsize) { + return PyBytes_FromStringAndSize(self->ob_item, Py_SIZE(self) * self->ob_descr->itemsize); } else { return PyErr_NoMemory(); } } -PyDoc_STRVAR(tostring_doc, -"tostring() -> string\n\ +PyDoc_STRVAR(tobytes_doc, +"tobytes() -> bytes\n\ \n\ -Convert the array to an array of machine values and return the string\n\ +Convert the array to an array of machine values and return the bytes\n\ representation."); +static PyObject * +array_tostring(arrayobject *self, PyObject *unused) +{ + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "tostring() is deprecated. Use tobytes() instead.", 2) != 0) + return NULL; + return array_tobytes(self, unused); +} + +PyDoc_STRVAR(tostring_doc, +"tostring() -> bytes\n\ +\n\ +Convert the array to an array of machine values and return the bytes\n\ +representation.\n\ +\n\ +This method is deprecated. Use tobytes instead."); + -#ifdef Py_USING_UNICODE static PyObject * array_fromunicode(arrayobject *self, PyObject *args) { Py_UNICODE *ustr; Py_ssize_t n; + Py_UNICODE typecode; if (!PyArg_ParseTuple(args, "u#:fromunicode", &ustr, &n)) return NULL; - if (self->ob_descr->typecode != 'u') { + typecode = self->ob_descr->typecode; + if (typecode != 'u') { PyErr_SetString(PyExc_ValueError, "fromunicode() may only be called on " - "type 'u' arrays"); + "unicode type arrays"); return NULL; } if (n > 0) { - Py_UNICODE *item = (Py_UNICODE *) self->ob_item; - if (Py_SIZE(self) > PY_SSIZE_T_MAX - n) { - return PyErr_NoMemory(); - } - PyMem_RESIZE(item, Py_UNICODE, Py_SIZE(self) + n); - if (item == NULL) { - PyErr_NoMemory(); + Py_ssize_t old_size = Py_SIZE(self); + if (array_resize(self, old_size + n) == -1) return NULL; - } - self->ob_item = (char *) item; - Py_SIZE(self) += n; - self->allocated = Py_SIZE(self); - memcpy(item + Py_SIZE(self) - n, + memcpy(self->ob_item + old_size * sizeof(Py_UNICODE), ustr, n * sizeof(Py_UNICODE)); } @@ -1473,17 +1483,19 @@ PyDoc_STRVAR(fromunicode_doc, "fromunicode(ustr)\n\ \n\ Extends this array with data from the unicode string ustr.\n\ -The array must be a type 'u' array; otherwise a ValueError\n\ -is raised. Use array.fromstring(ustr.decode(...)) to\n\ +The array must be a unicode type array; otherwise a ValueError\n\ +is raised. Use array.frombytes(ustr.encode(...)) to\n\ append Unicode data to an array of some other type."); static PyObject * array_tounicode(arrayobject *self, PyObject *unused) { - if (self->ob_descr->typecode != 'u') { + Py_UNICODE typecode; + typecode = self->ob_descr->typecode; + if (typecode != 'u') { PyErr_SetString(PyExc_ValueError, - "tounicode() may only be called on type 'u' arrays"); + "tounicode() may only be called on unicode type arrays"); return NULL; } return PyUnicode_FromUnicode((Py_UNICODE *) self->ob_item, Py_SIZE(self)); @@ -1493,16 +1505,471 @@ PyDoc_STRVAR(tounicode_doc, "tounicode() -> unicode\n\ \n\ Convert the array to a unicode string. The array must be\n\ -a type 'u' array; otherwise a ValueError is raised. Use\n\ -array.tostring().decode() to obtain a unicode string from\n\ +a unicode type array; otherwise a ValueError is raised. Use\n\ +array.tobytes().decode() to obtain a unicode string from\n\ an array of some other type."); -#endif /* Py_USING_UNICODE */ static PyObject * -array_reduce(arrayobject *array) +array_sizeof(arrayobject *self, PyObject *unused) +{ + Py_ssize_t res; + res = sizeof(arrayobject) + self->allocated * self->ob_descr->itemsize; + return PyLong_FromSsize_t(res); +} + +PyDoc_STRVAR(sizeof_doc, +"__sizeof__() -> int\n\ +\n\ +Size of the array in memory, in bytes."); + + +/*********************** Pickling support ************************/ + +enum machine_format_code { + UNKNOWN_FORMAT = -1, + /* UNKNOWN_FORMAT is used to indicate that the machine format for an + * array type code cannot be interpreted. When this occurs, a list of + * Python objects is used to represent the content of the array + * instead of using the memory content of the array directly. In that + * case, the array_reconstructor mechanism is bypassed completely, and + * the standard array constructor is used instead. + * + * This is will most likely occur when the machine doesn't use IEEE + * floating-point numbers. + */ + + UNSIGNED_INT8 = 0, + SIGNED_INT8 = 1, + UNSIGNED_INT16_LE = 2, + UNSIGNED_INT16_BE = 3, + SIGNED_INT16_LE = 4, + SIGNED_INT16_BE = 5, + UNSIGNED_INT32_LE = 6, + UNSIGNED_INT32_BE = 7, + SIGNED_INT32_LE = 8, + SIGNED_INT32_BE = 9, + UNSIGNED_INT64_LE = 10, + UNSIGNED_INT64_BE = 11, + SIGNED_INT64_LE = 12, + SIGNED_INT64_BE = 13, + IEEE_754_FLOAT_LE = 14, + IEEE_754_FLOAT_BE = 15, + IEEE_754_DOUBLE_LE = 16, + IEEE_754_DOUBLE_BE = 17, + UTF16_LE = 18, + UTF16_BE = 19, + UTF32_LE = 20, + UTF32_BE = 21 +}; +#define MACHINE_FORMAT_CODE_MIN 0 +#define MACHINE_FORMAT_CODE_MAX 21 + +static const struct mformatdescr { + size_t size; + int is_signed; + int is_big_endian; +} mformat_descriptors[] = { + {1, 0, 0}, /* 0: UNSIGNED_INT8 */ + {1, 1, 0}, /* 1: SIGNED_INT8 */ + {2, 0, 0}, /* 2: UNSIGNED_INT16_LE */ + {2, 0, 1}, /* 3: UNSIGNED_INT16_BE */ + {2, 1, 0}, /* 4: SIGNED_INT16_LE */ + {2, 1, 1}, /* 5: SIGNED_INT16_BE */ + {4, 0, 0}, /* 6: UNSIGNED_INT32_LE */ + {4, 0, 1}, /* 7: UNSIGNED_INT32_BE */ + {4, 1, 0}, /* 8: SIGNED_INT32_LE */ + {4, 1, 1}, /* 9: SIGNED_INT32_BE */ + {8, 0, 0}, /* 10: UNSIGNED_INT64_LE */ + {8, 0, 1}, /* 11: UNSIGNED_INT64_BE */ + {8, 1, 0}, /* 12: SIGNED_INT64_LE */ + {8, 1, 1}, /* 13: SIGNED_INT64_BE */ + {4, 0, 0}, /* 14: IEEE_754_FLOAT_LE */ + {4, 0, 1}, /* 15: IEEE_754_FLOAT_BE */ + {8, 0, 0}, /* 16: IEEE_754_DOUBLE_LE */ + {8, 0, 1}, /* 17: IEEE_754_DOUBLE_BE */ + {4, 0, 0}, /* 18: UTF16_LE */ + {4, 0, 1}, /* 19: UTF16_BE */ + {8, 0, 0}, /* 20: UTF32_LE */ + {8, 0, 1} /* 21: UTF32_BE */ +}; + + +/* + * Internal: This function is used to find the machine format of a given + * array type code. This returns UNKNOWN_FORMAT when the machine format cannot + * be found. + */ +static enum machine_format_code +typecode_to_mformat_code(int typecode) { - PyObject *dict, *result, *list; +#ifdef WORDS_BIGENDIAN + const int is_big_endian = 1; +#else + const int is_big_endian = 0; +#endif + size_t intsize; + int is_signed; + + switch (typecode) { + case 'b': + return SIGNED_INT8; + case 'B': + return UNSIGNED_INT8; + + case 'u': + if (sizeof(Py_UNICODE) == 2) { + return UTF16_LE + is_big_endian; + } + if (sizeof(Py_UNICODE) == 4) { + return UTF32_LE + is_big_endian; + } + return UNKNOWN_FORMAT; + + case 'f': + if (sizeof(float) == 4) { + const float y = 16711938.0; + if (memcmp(&y, "\x4b\x7f\x01\x02", 4) == 0) + return IEEE_754_FLOAT_BE; + if (memcmp(&y, "\x02\x01\x7f\x4b", 4) == 0) + return IEEE_754_FLOAT_LE; + } + return UNKNOWN_FORMAT; + + case 'd': + if (sizeof(double) == 8) { + const double x = 9006104071832581.0; + if (memcmp(&x, "\x43\x3f\xff\x01\x02\x03\x04\x05", 8) == 0) + return IEEE_754_DOUBLE_BE; + if (memcmp(&x, "\x05\x04\x03\x02\x01\xff\x3f\x43", 8) == 0) + return IEEE_754_DOUBLE_LE; + } + return UNKNOWN_FORMAT; + + /* Integers */ + case 'h': + intsize = sizeof(short); + is_signed = 1; + break; + case 'H': + intsize = sizeof(short); + is_signed = 0; + break; + case 'i': + intsize = sizeof(int); + is_signed = 1; + break; + case 'I': + intsize = sizeof(int); + is_signed = 0; + break; + case 'l': + intsize = sizeof(long); + is_signed = 1; + break; + case 'L': + intsize = sizeof(long); + is_signed = 0; + break; + default: + return UNKNOWN_FORMAT; + } + switch (intsize) { + case 2: + return UNSIGNED_INT16_LE + is_big_endian + (2 * is_signed); + case 4: + return UNSIGNED_INT32_LE + is_big_endian + (2 * is_signed); + case 8: + return UNSIGNED_INT64_LE + is_big_endian + (2 * is_signed); + default: + return UNKNOWN_FORMAT; + } +} + +/* Forward declaration. */ +static PyObject *array_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + +/* + * Internal: This function wraps the array constructor--i.e., array_new()--to + * allow the creation of array objects from C code without having to deal + * directly the tuple argument of array_new(). The typecode argument is a + * Unicode character value, like 'i' or 'f' for example, representing an array + * type code. The items argument is a bytes or a list object from which + * contains the initial value of the array. + * + * On success, this functions returns the array object created. Otherwise, + * NULL is returned to indicate a failure. + */ +static PyObject * +make_array(PyTypeObject *arraytype, Py_UNICODE typecode, PyObject *items) +{ + PyObject *new_args; + PyObject *array_obj; + PyObject *typecode_obj; + + assert(arraytype != NULL); + assert(items != NULL); + + typecode_obj = PyUnicode_FromUnicode(&typecode, 1); + if (typecode_obj == NULL) + return NULL; + + new_args = PyTuple_New(2); + if (new_args == NULL) + return NULL; + Py_INCREF(items); + PyTuple_SET_ITEM(new_args, 0, typecode_obj); + PyTuple_SET_ITEM(new_args, 1, items); + + array_obj = array_new(arraytype, new_args, NULL); + Py_DECREF(new_args); + if (array_obj == NULL) + return NULL; + + return array_obj; +} + +/* + * This functions is a special constructor used when unpickling an array. It + * provides a portable way to rebuild an array from its memory representation. + */ +static PyObject * +array_reconstructor(PyObject *self, PyObject *args) +{ + PyTypeObject *arraytype; + PyObject *items; + PyObject *converted_items; + PyObject *result; + int typecode_int; + Py_UNICODE typecode; + enum machine_format_code mformat_code; + struct arraydescr *descr; + + if (!PyArg_ParseTuple(args, "OCiO:array._array_reconstructor", + &arraytype, &typecode_int, &mformat_code, &items)) + return NULL; + + typecode = (Py_UNICODE)typecode_int; + + if (!PyType_Check(arraytype)) { + PyErr_Format(PyExc_TypeError, + "first argument must a type object, not %.200s", + Py_TYPE(arraytype)->tp_name); + return NULL; + } + if (!PyType_IsSubtype(arraytype, &Arraytype)) { + PyErr_Format(PyExc_TypeError, + "%.200s is not a subtype of %.200s", + arraytype->tp_name, Arraytype.tp_name); + return NULL; + } + for (descr = descriptors; descr->typecode != '\0'; descr++) { + if (descr->typecode == typecode) + break; + } + if (descr->typecode == '\0') { + PyErr_SetString(PyExc_ValueError, + "second argument must be a valid type code"); + return NULL; + } + if (mformat_code < MACHINE_FORMAT_CODE_MIN || + mformat_code > MACHINE_FORMAT_CODE_MAX) { + PyErr_SetString(PyExc_ValueError, + "third argument must be a valid machine format code."); + return NULL; + } + if (!PyBytes_Check(items)) { + PyErr_Format(PyExc_TypeError, + "fourth argument should be bytes, not %.200s", + Py_TYPE(items)->tp_name); + return NULL; + } + + /* Fast path: No decoding has to be done. */ + if (mformat_code == typecode_to_mformat_code(typecode) || + mformat_code == UNKNOWN_FORMAT) { + return make_array(arraytype, typecode, items); + } + + /* Slow path: Decode the byte string according to the given machine + * format code. This occurs when the computer unpickling the array + * object is architecturally different from the one that pickled the + * array. + */ + if (Py_SIZE(items) % mformat_descriptors[mformat_code].size != 0) { + PyErr_SetString(PyExc_ValueError, + "string length not a multiple of item size"); + return NULL; + } + switch (mformat_code) { + case IEEE_754_FLOAT_LE: + case IEEE_754_FLOAT_BE: { + int i; + int le = (mformat_code == IEEE_754_FLOAT_LE) ? 1 : 0; + Py_ssize_t itemcount = Py_SIZE(items) / 4; + const unsigned char *memstr = + (unsigned char *)PyBytes_AS_STRING(items); + + converted_items = PyList_New(itemcount); + if (converted_items == NULL) + return NULL; + for (i = 0; i < itemcount; i++) { + PyObject *pyfloat = PyFloat_FromDouble( + _PyFloat_Unpack4(&memstr[i * 4], le)); + if (pyfloat == NULL) { + Py_DECREF(converted_items); + return NULL; + } + PyList_SET_ITEM(converted_items, i, pyfloat); + } + break; + } + case IEEE_754_DOUBLE_LE: + case IEEE_754_DOUBLE_BE: { + int i; + int le = (mformat_code == IEEE_754_DOUBLE_LE) ? 1 : 0; + Py_ssize_t itemcount = Py_SIZE(items) / 8; + const unsigned char *memstr = + (unsigned char *)PyBytes_AS_STRING(items); + + converted_items = PyList_New(itemcount); + if (converted_items == NULL) + return NULL; + for (i = 0; i < itemcount; i++) { + PyObject *pyfloat = PyFloat_FromDouble( + _PyFloat_Unpack8(&memstr[i * 8], le)); + if (pyfloat == NULL) { + Py_DECREF(converted_items); + return NULL; + } + PyList_SET_ITEM(converted_items, i, pyfloat); + } + break; + } + case UTF16_LE: + case UTF16_BE: { + int byteorder = (mformat_code == UTF16_LE) ? -1 : 1; + converted_items = PyUnicode_DecodeUTF16( + PyBytes_AS_STRING(items), Py_SIZE(items), + "strict", &byteorder); + if (converted_items == NULL) + return NULL; + break; + } + case UTF32_LE: + case UTF32_BE: { + int byteorder = (mformat_code == UTF32_LE) ? -1 : 1; + converted_items = PyUnicode_DecodeUTF32( + PyBytes_AS_STRING(items), Py_SIZE(items), + "strict", &byteorder); + if (converted_items == NULL) + return NULL; + break; + } + + case UNSIGNED_INT8: + case SIGNED_INT8: + case UNSIGNED_INT16_LE: + case UNSIGNED_INT16_BE: + case SIGNED_INT16_LE: + case SIGNED_INT16_BE: + case UNSIGNED_INT32_LE: + case UNSIGNED_INT32_BE: + case SIGNED_INT32_LE: + case SIGNED_INT32_BE: + case UNSIGNED_INT64_LE: + case UNSIGNED_INT64_BE: + case SIGNED_INT64_LE: + case SIGNED_INT64_BE: { + int i; + const struct mformatdescr mf_descr = + mformat_descriptors[mformat_code]; + Py_ssize_t itemcount = Py_SIZE(items) / mf_descr.size; + const unsigned char *memstr = + (unsigned char *)PyBytes_AS_STRING(items); + struct arraydescr *descr; + + /* If possible, try to pack array's items using a data type + * that fits better. This may result in an array with narrower + * or wider elements. + * + * For example, if a 32-bit machine pickles a L-code array of + * unsigned longs, then the array will be unpickled by 64-bit + * machine as an I-code array of unsigned ints. + * + * XXX: Is it possible to write a unit test for this? + */ + for (descr = descriptors; descr->typecode != '\0'; descr++) { + if (descr->is_integer_type && + descr->itemsize == mf_descr.size && + descr->is_signed == mf_descr.is_signed) + typecode = descr->typecode; + } + + converted_items = PyList_New(itemcount); + if (converted_items == NULL) + return NULL; + for (i = 0; i < itemcount; i++) { + PyObject *pylong; + + pylong = _PyLong_FromByteArray( + &memstr[i * mf_descr.size], + mf_descr.size, + !mf_descr.is_big_endian, + mf_descr.is_signed); + if (pylong == NULL) { + Py_DECREF(converted_items); + return NULL; + } + PyList_SET_ITEM(converted_items, i, pylong); + } + break; + } + case UNKNOWN_FORMAT: + /* Impossible, but needed to shut up GCC about the unhandled + * enumeration value. + */ + default: + PyErr_BadArgument(); + return NULL; + } + + result = make_array(arraytype, typecode, converted_items); + Py_DECREF(converted_items); + return result; +} + +static PyObject * +array_reduce_ex(arrayobject *array, PyObject *value) +{ + PyObject *dict; + PyObject *result; + PyObject *array_str; + int typecode = array->ob_descr->typecode; + int mformat_code; + static PyObject *array_reconstructor = NULL; + long protocol; + + if (array_reconstructor == NULL) { + PyObject *array_module = PyImport_ImportModule("array"); + if (array_module == NULL) + return NULL; + array_reconstructor = PyObject_GetAttrString( + array_module, + "_array_reconstructor"); + Py_DECREF(array_module); + if (array_reconstructor == NULL) + return NULL; + } + + if (!PyLong_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "__reduce_ex__ argument should an integer"); + return NULL; + } + protocol = PyLong_AsLong(value); + if (protocol == -1 && PyErr_Occurred()) + return NULL; dict = PyObject_GetAttrString((PyObject *)array, "__dict__"); if (dict == NULL) { @@ -1512,20 +1979,41 @@ array_reduce(arrayobject *array) dict = Py_None; Py_INCREF(dict); } - /* Unlike in Python 3.x, we never use the more efficient memory - * representation of an array for pickling. This is unfortunately - * necessary to allow array objects to be unpickled by Python 3.x, - * since str objects from 2.x are always decoded to unicode in - * Python 3.x. - */ - list = array_tolist(array, NULL); - if (list == NULL) { + + mformat_code = typecode_to_mformat_code(typecode); + if (mformat_code == UNKNOWN_FORMAT || protocol < 3) { + /* Convert the array to a list if we got something weird + * (e.g., non-IEEE floats), or we are pickling the array using + * a Python 2.x compatible protocol. + * + * It is necessary to use a list representation for Python 2.x + * compatible pickle protocol, since Python 2's str objects + * are unpickled as unicode by Python 3. Thus it is impossible + * to make arrays unpicklable by Python 3 by using their memory + * representation, unless we resort to ugly hacks such as + * coercing unicode objects to bytes in array_reconstructor. + */ + PyObject *list; + list = array_tolist(array, NULL); + if (list == NULL) { + Py_DECREF(dict); + return NULL; + } + result = Py_BuildValue( + "O(CO)O", Py_TYPE(array), typecode, list, dict); + Py_DECREF(list); + Py_DECREF(dict); + return result; + } + + array_str = array_tobytes(array, NULL); + if (array_str == NULL) { Py_DECREF(dict); return NULL; } result = Py_BuildValue( - "O(cO)O", Py_TYPE(array), array->ob_descr->typecode, list, dict); - Py_DECREF(list); + "O(OCiN)O", array_reconstructor, Py_TYPE(array), typecode, + mformat_code, array_str, dict); Py_DECREF(dict); return result; } @@ -1533,29 +2021,16 @@ array_reduce(arrayobject *array) PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyObject * -array_sizeof(arrayobject *self, PyObject *unused) -{ - Py_ssize_t res; - res = sizeof(arrayobject) + self->allocated * self->ob_descr->itemsize; - return PyLong_FromSsize_t(res); -} - -PyDoc_STRVAR(sizeof_doc, -"__sizeof__() -> int\n\ -\n\ -Size of the array in memory, in bytes."); - -static PyObject * array_get_typecode(arrayobject *a, void *closure) { - char tc = a->ob_descr->typecode; - return PyString_FromStringAndSize(&tc, 1); + Py_UNICODE tc = a->ob_descr->typecode; + return PyUnicode_FromUnicode(&tc, 1); } static PyObject * array_get_itemsize(arrayobject *a, void *closure) { - return PyInt_FromLong((long)a->ob_descr->itemsize); + return PyLong_FromLong((long)a->ob_descr->itemsize); } static PyGetSetDef array_getsets [] = { @@ -1587,19 +2062,17 @@ static PyMethodDef array_methods[] = { fromlist_doc}, {"fromstring", (PyCFunction)array_fromstring, METH_VARARGS, fromstring_doc}, -#ifdef Py_USING_UNICODE + {"frombytes", (PyCFunction)array_frombytes, METH_VARARGS, + frombytes_doc}, {"fromunicode", (PyCFunction)array_fromunicode, METH_VARARGS, fromunicode_doc}, -#endif {"index", (PyCFunction)array_index, METH_O, index_doc}, {"insert", (PyCFunction)array_insert, METH_VARARGS, insert_doc}, {"pop", (PyCFunction)array_pop, METH_VARARGS, pop_doc}, - {"read", (PyCFunction)array_fromfile_as_read, METH_VARARGS, - fromfile_doc}, - {"__reduce__", (PyCFunction)array_reduce, METH_NOARGS, + {"__reduce_ex__", (PyCFunction)array_reduce_ex, METH_O, reduce_doc}, {"remove", (PyCFunction)array_remove, METH_O, remove_doc}, @@ -1613,12 +2086,10 @@ static PyMethodDef array_methods[] = { tolist_doc}, {"tostring", (PyCFunction)array_tostring, METH_NOARGS, tostring_doc}, -#ifdef Py_USING_UNICODE + {"tobytes", (PyCFunction)array_tobytes, METH_NOARGS, + tobytes_doc}, {"tounicode", (PyCFunction)array_tounicode, METH_NOARGS, tounicode_doc}, -#endif - {"write", (PyCFunction)array_tofile_as_write, METH_O, - tofile_doc}, {"__sizeof__", (PyCFunction)array_sizeof, METH_NOARGS, sizeof_doc}, {NULL, NULL} /* sentinel */ @@ -1627,32 +2098,23 @@ static PyMethodDef array_methods[] = { static PyObject * array_repr(arrayobject *a) { - char buf[256], typecode; - PyObject *s, *t, *v = NULL; + Py_UNICODE typecode; + PyObject *s, *v = NULL; Py_ssize_t len; len = Py_SIZE(a); typecode = a->ob_descr->typecode; if (len == 0) { - PyOS_snprintf(buf, sizeof(buf), "array('%c')", typecode); - return PyString_FromString(buf); + return PyUnicode_FromFormat("array('%c')", (int)typecode); } - - if (typecode == 'c') - v = array_tostring(a, NULL); -#ifdef Py_USING_UNICODE - else if (typecode == 'u') + if (typecode == 'u') { v = array_tounicode(a, NULL); -#endif - else + } else { v = array_tolist(a, NULL); - t = PyObject_Repr(v); - Py_XDECREF(v); + } - PyOS_snprintf(buf, sizeof(buf), "array('%c', ", typecode); - s = PyString_FromString(buf); - PyString_ConcatAndDel(&s, t); - PyString_ConcatAndDel(&s, PyString_FromString(")")); + s = PyUnicode_FromFormat("array('%c', %R)", (int)typecode, v); + Py_DECREF(v); return s; } @@ -1674,7 +2136,7 @@ array_subscr(arrayobject* self, PyObject* item) arrayobject* ar; int itemsize = self->ob_descr->itemsize; - if (PySlice_GetIndicesEx((PySliceObject*)item, Py_SIZE(self), + if (PySlice_GetIndicesEx(item, Py_SIZE(self), &start, &stop, &step, &slicelength) < 0) { return NULL; } @@ -1745,7 +2207,7 @@ array_ass_subscr(arrayobject* self, PyObject* item, PyObject* value) return (*self->ob_descr->setitem)(self, i, value); } else if (PySlice_Check(item)) { - if (PySlice_GetIndicesEx((PySliceObject *)item, + if (PySlice_GetIndicesEx(item, Py_SIZE(self), &start, &stop, &step, &slicelength) < 0) { return -1; @@ -1789,18 +2251,28 @@ array_ass_subscr(arrayobject* self, PyObject* item, PyObject* value) if ((step > 0 && stop < start) || (step < 0 && stop > start)) stop = start; + + /* Issue #4509: If the array has exported buffers and the slice + assignment would change the size of the array, fail early to make + sure we don't modify it. */ + if ((needed == 0 || slicelength != needed) && self->ob_exports > 0) { + PyErr_SetString(PyExc_BufferError, + "cannot resize an array that is exporting buffers"); + return -1; + } + if (step == 1) { if (slicelength > needed) { memmove(self->ob_item + (start + needed) * itemsize, self->ob_item + stop * itemsize, (Py_SIZE(self) - stop) * itemsize); if (array_resize(self, Py_SIZE(self) + - needed - slicelength) < 0) + needed - slicelength) < 0) return -1; } else if (slicelength < needed) { if (array_resize(self, Py_SIZE(self) + - needed - slicelength) < 0) + needed - slicelength) < 0) return -1; memmove(self->ob_item + (start + needed) * itemsize, self->ob_item + stop * itemsize, @@ -1869,40 +2341,49 @@ static PyMappingMethods array_as_mapping = { static const void *emptybuf = ""; -static Py_ssize_t -array_buffer_getreadbuf(arrayobject *self, Py_ssize_t index, const void **ptr) -{ - if ( index != 0 ) { - PyErr_SetString(PyExc_SystemError, - "Accessing non-existent array segment"); - return -1; - } - *ptr = (void *)self->ob_item; - if (*ptr == NULL) - *ptr = emptybuf; - return Py_SIZE(self)*self->ob_descr->itemsize; -} -static Py_ssize_t -array_buffer_getwritebuf(arrayobject *self, Py_ssize_t index, const void **ptr) +static int +array_buffer_getbuf(arrayobject *self, Py_buffer *view, int flags) { - if ( index != 0 ) { - PyErr_SetString(PyExc_SystemError, - "Accessing non-existent array segment"); - return -1; + if (view==NULL) goto finish; + + view->buf = (void *)self->ob_item; + view->obj = (PyObject*)self; + Py_INCREF(self); + if (view->buf == NULL) + view->buf = (void *)emptybuf; + view->len = (Py_SIZE(self)) * self->ob_descr->itemsize; + view->readonly = 0; + view->ndim = 1; + view->itemsize = self->ob_descr->itemsize; + view->suboffsets = NULL; + view->shape = NULL; + if ((flags & PyBUF_ND)==PyBUF_ND) { + view->shape = &((Py_SIZE(self))); + } + view->strides = NULL; + if ((flags & PyBUF_STRIDES)==PyBUF_STRIDES) + view->strides = &(view->itemsize); + view->format = NULL; + view->internal = NULL; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { + view->format = self->ob_descr->formats; +#ifdef Py_UNICODE_WIDE + if (self->ob_descr->typecode == 'u') { + view->format = "w"; + } +#endif } - *ptr = (void *)self->ob_item; - if (*ptr == NULL) - *ptr = emptybuf; - return Py_SIZE(self)*self->ob_descr->itemsize; + + finish: + self->ob_exports++; + return 0; } -static Py_ssize_t -array_buffer_getsegcount(arrayobject *self, Py_ssize_t *lenp) +static void +array_buffer_relbuf(arrayobject *self, Py_buffer *view) { - if ( lenp ) - *lenp = Py_SIZE(self)*self->ob_descr->itemsize; - return 1; + self->ob_exports--; } static PySequenceMethods array_as_sequence = { @@ -1910,37 +2391,39 @@ static PySequenceMethods array_as_sequence = { (binaryfunc)array_concat, /*sq_concat*/ (ssizeargfunc)array_repeat, /*sq_repeat*/ (ssizeargfunc)array_item, /*sq_item*/ - (ssizessizeargfunc)array_slice, /*sq_slice*/ + 0, /*sq_slice*/ (ssizeobjargproc)array_ass_item, /*sq_ass_item*/ - (ssizessizeobjargproc)array_ass_slice, /*sq_ass_slice*/ + 0, /*sq_ass_slice*/ (objobjproc)array_contains, /*sq_contains*/ (binaryfunc)array_inplace_concat, /*sq_inplace_concat*/ (ssizeargfunc)array_inplace_repeat /*sq_inplace_repeat*/ }; static PyBufferProcs array_as_buffer = { - (readbufferproc)array_buffer_getreadbuf, - (writebufferproc)array_buffer_getwritebuf, - (segcountproc)array_buffer_getsegcount, - NULL, + (getbufferproc)array_buffer_getbuf, + (releasebufferproc)array_buffer_relbuf }; static PyObject * array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - char c; + int c; PyObject *initial = NULL, *it = NULL; struct arraydescr *descr; if (type == &Arraytype && !_PyArg_NoKeywords("array.array()", kwds)) return NULL; - if (!PyArg_ParseTuple(args, "c|O:array", &c, &initial)) + if (!PyArg_ParseTuple(args, "C|O:array", &c, &initial)) return NULL; if (!(initial == NULL || PyList_Check(initial) - || PyString_Check(initial) || PyTuple_Check(initial) - || (c == 'u' && PyUnicode_Check(initial)))) { + || PyByteArray_Check(initial) + || PyBytes_Check(initial) + || PyTuple_Check(initial) + || ((c=='u') && PyUnicode_Check(initial)) + || (array_Check(initial) + && c == ((arrayobject*)initial)->ob_descr->typecode))) { it = PyObject_GetIter(initial); if (it == NULL) return NULL; @@ -1956,17 +2439,20 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyObject *a; Py_ssize_t len; - if (initial == NULL || !(PyList_Check(initial) - || PyTuple_Check(initial))) + if (initial == NULL) len = 0; + else if (PyList_Check(initial)) + len = PyList_GET_SIZE(initial); + else if (PyTuple_Check(initial) || array_Check(initial)) + len = Py_SIZE(initial); else - len = PySequence_Size(initial); + len = 0; a = newarrayobject(type, len, descr); if (a == NULL) return NULL; - if (len > 0) { + if (len > 0 && !array_Check(initial)) { Py_ssize_t i; for (i = 0; i < len; i++) { PyObject *v = @@ -1982,14 +2468,16 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } Py_DECREF(v); } - } else if (initial != NULL && PyString_Check(initial)) { + } + else if (initial != NULL && (PyByteArray_Check(initial) || + PyBytes_Check(initial))) { PyObject *t_initial, *v; t_initial = PyTuple_Pack(1, initial); if (t_initial == NULL) { Py_DECREF(a); return NULL; } - v = array_fromstring((arrayobject *)a, + v = array_frombytes((arrayobject *)a, t_initial); Py_DECREF(t_initial); if (v == NULL) { @@ -1997,8 +2485,8 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } Py_DECREF(v); -#ifdef Py_USING_UNICODE - } else if (initial != NULL && PyUnicode_Check(initial)) { + } + else if (initial != NULL && PyUnicode_Check(initial)) { Py_ssize_t n = PyUnicode_GET_DATA_SIZE(initial); if (n > 0) { arrayobject *self = (arrayobject *)a; @@ -2014,7 +2502,11 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) memcpy(item, PyUnicode_AS_DATA(initial), n); self->allocated = Py_SIZE(self); } -#endif + } + else if (initial != NULL && array_Check(initial)) { + arrayobject *self = (arrayobject *)a; + arrayobject *other = (arrayobject *)initial; + memcpy(self->ob_item, other->ob_item, len * other->ob_descr->itemsize); } if (it != NULL) { if (array_iter_extend((arrayobject *)a, it) == -1) { @@ -2028,7 +2520,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } } PyErr_SetString(PyExc_ValueError, - "bad typecode (must be c, b, B, u, h, H, i, I, l, L, f or d)"); + "bad typecode (must be b, B, u, h, H, i, I, l, L, f or d)"); return NULL; } @@ -2042,10 +2534,9 @@ type is specified at object creation time by using a type code, which\n\ is a single character. The following type codes are defined:\n\ \n\ Type code C Type Minimum size in bytes \n\ - 'c' character 1 \n\ 'b' signed integer 1 \n\ 'B' unsigned integer 1 \n\ - 'u' Unicode character 2 \n\ + 'u' Unicode character 2 (see note) \n\ 'h' signed integer 2 \n\ 'H' unsigned integer 2 \n\ 'i' signed integer 2 \n\ @@ -2055,6 +2546,9 @@ is a single character. The following type codes are defined:\n\ 'f' floating point 4 \n\ 'd' floating point 8 \n\ \n\ +NOTE: The 'u' typecode corresponds to Python's unicode character. On \n\ +narrow builds this is 2-bytes on wide builds this is 4-bytes.\n\ +\n\ The constructor is:\n\ \n\ array(typecode [, initializer]) -- create a new array\n\ @@ -2079,17 +2573,15 @@ count() -- return number of occurrences of an object\n\ extend() -- extend array by appending multiple elements from an iterable\n\ fromfile() -- read items from a file object\n\ fromlist() -- append items from the list\n\ -fromstring() -- append items from the string\n\ +frombytes() -- append items from the string\n\ index() -- return index of first occurrence of an object\n\ insert() -- insert a new item into the array at a provided position\n\ pop() -- remove and return item (default last)\n\ -read() -- DEPRECATED, use fromfile()\n\ remove() -- remove first occurrence of an object\n\ reverse() -- reverse the order of the items in the array\n\ tofile() -- write all items to a file object\n\ tolist() -- return the array converted to an ordinary list\n\ -tostring() -- return the array converted to a string\n\ -write() -- DEPRECATED, use tofile()\n\ +tobytes() -- return the array converted to a string\n\ \n\ Attributes:\n\ \n\ @@ -2108,7 +2600,7 @@ static PyTypeObject Arraytype = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_compare */ + 0, /* tp_reserved */ (reprfunc)array_repr, /* tp_repr */ 0, /* tp_as_number*/ &array_as_sequence, /* tp_as_sequence*/ @@ -2119,7 +2611,7 @@ static PyTypeObject Arraytype = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ &array_as_buffer, /* tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ arraytype_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -2211,7 +2703,7 @@ static PyTypeObject PyArrayIter_Type = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_compare */ + 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -2238,24 +2730,60 @@ static PyTypeObject PyArrayIter_Type = { /* No functions in array module. */ static PyMethodDef a_methods[] = { + {"_array_reconstructor", array_reconstructor, METH_VARARGS, + PyDoc_STR("Internal. Used for pickling support.")}, {NULL, NULL, 0, NULL} /* Sentinel */ }; +static struct PyModuleDef arraymodule = { + PyModuleDef_HEAD_INIT, + "array", + module_doc, + -1, + a_methods, + NULL, + NULL, + NULL, + NULL +}; + PyMODINIT_FUNC -initarray(void) +PyInit_array(void) { PyObject *m; + PyObject *typecodes; + Py_ssize_t size = 0; + register Py_UNICODE *p; + struct arraydescr *descr; - Arraytype.ob_type = &PyType_Type; - PyArrayIter_Type.ob_type = &PyType_Type; - m = Py_InitModule3("array", a_methods, module_doc); + if (PyType_Ready(&Arraytype) < 0) + return NULL; + Py_TYPE(&PyArrayIter_Type) = &PyType_Type; + m = PyModule_Create(&arraymodule); if (m == NULL) - return; + return NULL; Py_INCREF((PyObject *)&Arraytype); PyModule_AddObject(m, "ArrayType", (PyObject *)&Arraytype); Py_INCREF((PyObject *)&Arraytype); PyModule_AddObject(m, "array", (PyObject *)&Arraytype); - /* No need to check the error here, the caller will do that */ + + for (descr=descriptors; descr->typecode != '\0'; descr++) { + size++; + } + + typecodes = PyUnicode_FromStringAndSize(NULL, size); + p = PyUnicode_AS_UNICODE(typecodes); + for (descr = descriptors; descr->typecode != '\0'; descr++) { + *p++ = (char)descr->typecode; + } + + PyModule_AddObject(m, "typecodes", (PyObject *)typecodes); + + if (PyErr_Occurred()) { + Py_DECREF(m); + m = NULL; + } + return m; } |