Repository: numpy/numpy
File: numpy/_core/src/multiarray/ctors.c (C)

Code before:
    memcpy(dims, PyArray_DIMS(ret), PyArray_NDIM(ret)*sizeof(npy_intp));
    PyArray_Dims new_dims = {dims, PyArray_NDIM(ret)};

    char *item = PyArray_BYTES(ret);
    for (i = 0; i < count || count == -1; i++, item += elsize) {
        PyObject *value = PyIter_Next(iter);
        if (value == NULL) {
            if (PyErr_Occurred()) {
                /* Fetching next item failed perhaps due to exhausting iterator */
                goto done;
            }
            break;
        }

        if (NPY_UNLIKELY(i >= elcount) && elsize != 0) {
            /*
              Grow PyArray_DATA(ret):
              this is similar for the strategy for PyListObject, but we use
              50% overallocation => 0, 4, 8, 14, 23, 36, 56, 86 ...
              TODO: The loadtxt code now uses a `growth` helper that would
                    be suitable to reuse here.
            */
            elcount = (i >> 1) + (i < 4 ? 4 : 2) + i;
            npy_intp nbytes = 0;
            PyObject *res = NULL;
            if (!npy_mul_sizes_with_overflow(&nbytes, elcount, elsize)) {
                dims[0] = elcount;
                res = PyArray_Resize(ret, &new_dims, 0, 0);
            }
            if (res == NULL) {
                Py_DECREF(value);
                goto done;
            }
            Py_DECREF(res);
            /* Reset `item` pointer to point into realloc'd chunk */
            item = ((char *)PyArray_DATA(ret)) + i * elsize;
        }

        if (PyArray_Pack(dtype, item, value) < 0) {
            Py_DECREF(value);
            goto done;
        }
        Py_DECREF(value);
    }

    if (i < count) {
        PyErr_Format(PyExc_ValueError,
                "iterator too short: Expected %zd but iterator had only %zd "
                "items.", (Py_ssize_t)count, (Py_ssize_t)i);
        goto done;
    }

Review comment (bug):
This `mul_sizes_with_overflow` protects from overflows (mainly on 32bit systems).
`PyArray_Resize()` *should* already do that (I dunno if I trust it to, though).

So this branch should be unnecessary now (which also cleans up the slightly weird error logic)

Code after:
    memcpy(dims, PyArray_DIMS(ret), PyArray_NDIM(ret)*sizeof(npy_intp));
    PyArray_Dims new_dims = {dims, PyArray_NDIM(ret)};

    char *item = PyArray_BYTES(ret);
    for (i = 0; i < count || count == -1; i++, item += elsize) {
        PyObject *value = PyIter_Next(iter);
        if (value == NULL) {
            if (PyErr_Occurred()) {
                /* Fetching next item failed perhaps due to exhausting iterator */
                goto fail;
            }
            break;
        }

        if (NPY_UNLIKELY(i >= elcount) && elsize != 0) {
            /*
              Grow PyArray_DATA(ret):
              this is similar for the strategy for PyListObject, but we use
              50% overallocation => 0, 4, 8, 14, 23, 36, 56, 86 ...
              TODO: The loadtxt code now uses a `growth` helper that would
                    be suitable to reuse here.
            */
            elcount = (i >> 1) + (i < 4 ? 4 : 2) + i;
            dims[0] = elcount;
            if (PyArray_Resize_int(ret, &new_dims, 0) < 0) {
                Py_DECREF(value);
                goto fail;
            }
            /* Reset `item` pointer to point into realloc'd chunk */
            item = ((char *)PyArray_DATA(ret)) + i * elsize;
        }

        if (PyArray_Pack(dtype, item, value) < 0) {
            Py_DECREF(value);
            goto fail;
        }
        Py_DECREF(value);
    }

    if (i < count) {
        PyErr_Format(PyExc_ValueError,
                "iterator too short: Expected %zd but iterator had only %zd "
                "items.", (Py_ssize_t)count, (Py_ssize_t)i);
        goto fail;
    }

    /*
     * Realloc the data so that don't keep extra memory tied up and fix
     * the arrays first dimension (there could be more than one).
     */
    dims[0] = i;