]> granicus.if.org Git - python/commitdiff
Issue #6688: Optimize PyBytes_FromObject().
authorAlexandre Vassalotti <alexandre@peadrop.com>
Sat, 9 Jan 2010 22:14:46 +0000 (22:14 +0000)
committerAlexandre Vassalotti <alexandre@peadrop.com>
Sat, 9 Jan 2010 22:14:46 +0000 (22:14 +0000)
  - Add special-cases for list and tuple objects.
  - Use _PyObject_LengthHint() instead of an arbitrary value for the
    size of the initial buffer of the returned object.

Objects/bytesobject.c

index cb634481bd422891e333672951d713b23d5bfa37..eb11940ccfe3fd004f72b4586fa29a863d3f4371 100644 (file)
@@ -2979,17 +2979,62 @@ PyBytes_FromObject(PyObject *x)
                return NULL;
        }
 
+       if (PyList_CheckExact(x)) {
+               new = PyBytes_FromStringAndSize(NULL, Py_SIZE(x));
+               if (new == NULL)
+                       return NULL;
+               for (i = 0; i < Py_SIZE(x); i++) {
+                       Py_ssize_t value = PyNumber_AsSsize_t(
+                               PyList_GET_ITEM(x, i), PyExc_ValueError);
+                       if (value == -1 && PyErr_Occurred()) {
+                               Py_DECREF(new);
+                               return NULL;
+                       }
+                       if (value < 0 || value >= 256) {
+                               PyErr_SetString(PyExc_ValueError,
+                                               "bytes must be in range(0, 256)");
+                               Py_DECREF(new);
+                               return NULL;
+                       }
+                       ((PyBytesObject *)new)->ob_sval[i] = value;                     
+               }
+               return new;
+       }
+       if (PyTuple_CheckExact(x)) {
+               new = PyBytes_FromStringAndSize(NULL, Py_SIZE(x));
+               if (new == NULL)
+                       return NULL;
+               for (i = 0; i < Py_SIZE(x); i++) {
+                       Py_ssize_t value = PyNumber_AsSsize_t(
+                               PyTuple_GET_ITEM(x, i), PyExc_ValueError);
+                       if (value == -1 && PyErr_Occurred()) {
+                               Py_DECREF(new);
+                               return NULL;
+                       }
+                       if (value < 0 || value >= 256) {
+                               PyErr_SetString(PyExc_ValueError,
+                                               "bytes must be in range(0, 256)");
+                               Py_DECREF(new);
+                               return NULL;
+                       }
+                       ((PyBytesObject *)new)->ob_sval[i] = value;                     
+               }
+               return new;
+       }
+
        /* For iterator version, create a string object and resize as needed */
-       /* XXX(gb): is 64 a good value? also, optimize if length is known */
-       /* XXX(guido): perhaps use Pysequence_Fast() -- I can't imagine the
-          input being a truly long iterator. */
-       size = 64;
+       size = _PyObject_LengthHint(x, 64);
+       if (size == -1 && PyErr_Occurred())
+               return NULL;
+       /* Allocate an extra byte to prevent PyBytes_FromStringAndSize() from
+          returning a shared empty bytes string. This required because we
+          want to call _PyBytes_Resize() the returned object, which we can
+          only do on bytes objects with refcount == 1. */
+       size += 1;
        new = PyBytes_FromStringAndSize(NULL, size);
        if (new == NULL)
                return NULL;
 
-       /* XXX Optimize this if the arguments is a list, tuple */
-
        /* Get the iterator */
        it = PyObject_GetIter(x);
        if (it == NULL)
@@ -3023,7 +3068,7 @@ PyBytes_FromObject(PyObject *x)
 
                /* Append the byte */
                if (i >= size) {
-                       size *= 2;
+                       size = 2 * size + 1;
                        if (_PyBytes_Resize(&new, size) < 0)
                                goto error;
                }