@@ -1845,77 +1845,116 @@ array_reduce_ex_regular(PyArrayObject *self, int NPY_UNUSED(protocol))
1845
1845
static PyObject *
1846
1846
array_reduce_ex_picklebuffer (PyArrayObject * self , int protocol )
1847
1847
{
1848
- PyObject * numeric_mod = NULL , * from_buffer_func = NULL ;
1849
- PyObject * pickle_module = NULL , * picklebuf_class = NULL ;
1850
- PyObject * picklebuf_args = NULL ;
1848
+ PyObject * from_buffer_func = NULL ;
1849
+ PyObject * picklebuf_class = NULL ;
1851
1850
PyObject * buffer = NULL , * transposed_array = NULL ;
1852
1851
PyArray_Descr * descr = NULL ;
1852
+ PyObject * rev_perm = NULL ; // only used in 'K' order
1853
1853
char order ;
1854
1854
1855
1855
descr = PyArray_DESCR (self );
1856
1856
1857
1857
/* we expect protocol 5 to be available in Python 3.8 */
1858
- pickle_module = PyImport_ImportModule ("pickle" );
1859
- if (pickle_module == NULL ){
1860
- return NULL ;
1861
- }
1862
- picklebuf_class = PyObject_GetAttrString (pickle_module , "PickleBuffer" );
1863
- Py_DECREF (pickle_module );
1864
- if (picklebuf_class == NULL ) {
1858
+ if (npy_cache_import_runtime ("pickle" , "PickleBuffer" , & picklebuf_class ) == -1 ) {
1865
1859
return NULL ;
1866
1860
}
1867
1861
1868
1862
/* Construct a PickleBuffer of the array */
1869
-
1870
- if (!PyArray_IS_C_CONTIGUOUS ((PyArrayObject * ) self ) &&
1871
- PyArray_IS_F_CONTIGUOUS ((PyArrayObject * ) self )) {
1863
+ if (PyArray_IS_C_CONTIGUOUS ((PyArrayObject * )self )) {
1864
+ order = 'C' ;
1865
+ }
1866
+ else if (PyArray_IS_F_CONTIGUOUS ((PyArrayObject * )self )) {
1872
1867
/* if the array if Fortran-contiguous and not C-contiguous,
1873
1868
* the PickleBuffer instance will hold a view on the transpose
1874
1869
* of the initial array, that is C-contiguous. */
1875
1870
order = 'F' ;
1876
- transposed_array = PyArray_Transpose ((PyArrayObject * )self , NULL );
1877
- picklebuf_args = Py_BuildValue ("(N)" , transposed_array );
1871
+ transposed_array = PyArray_Transpose ((PyArrayObject * )self , NULL );
1872
+ if (transposed_array == NULL ) {
1873
+ return NULL ;
1874
+ }
1878
1875
}
1879
1876
else {
1880
- order = 'C' ;
1881
- picklebuf_args = Py_BuildValue ("(O)" , self );
1882
- }
1883
- if (picklebuf_args == NULL ) {
1884
- Py_DECREF (picklebuf_class );
1885
- return NULL ;
1877
+ order = 'K' ;
1878
+ const int n = PyArray_NDIM (self );
1879
+ npy_stride_sort_item items [NPY_MAXDIMS ];
1880
+ // sort (strde, perm) as descending = transpose to C
1881
+ PyArray_CreateSortedStridePerm (n , PyArray_STRIDES (self ), items );
1882
+ rev_perm = PyTuple_New (n );
1883
+ if (rev_perm == NULL ) {
1884
+ return NULL ;
1885
+ }
1886
+ PyArray_Dims perm ;
1887
+ npy_intp dims [NPY_MAXDIMS ];
1888
+ for (int i = 0 ; i < n ; i ++ ) {
1889
+ dims [i ] = items [i ].perm ;
1890
+ PyObject * idx = PyLong_FromLong (i );
1891
+ if (idx == NULL ) {
1892
+ Py_DECREF (rev_perm );
1893
+ return NULL ;
1894
+ }
1895
+ PyTuple_SET_ITEM (rev_perm , items [i ].perm , idx );
1896
+ }
1897
+ perm .ptr = dims ;
1898
+ perm .len = n ;
1899
+ transposed_array = PyArray_Transpose ((PyArrayObject * )self , & perm );
1900
+ if (transposed_array == NULL ) {
1901
+ Py_DECREF (rev_perm );
1902
+ return NULL ;
1903
+ }
1904
+ if (!PyArray_IS_C_CONTIGUOUS ((PyArrayObject * )transposed_array )) {
1905
+ // self is non-contiguous
1906
+ Py_DECREF (rev_perm );
1907
+ Py_DECREF (transposed_array );
1908
+ return array_reduce_ex_regular (self , protocol );
1909
+ }
1886
1910
}
1887
-
1888
- buffer = PyObject_CallObject (picklebuf_class , picklebuf_args );
1889
- Py_DECREF (picklebuf_class );
1890
- Py_DECREF (picklebuf_args );
1911
+ buffer = PyObject_CallOneArg (picklebuf_class , transposed_array == NULL ? (PyObject * ) self : transposed_array );
1891
1912
if (buffer == NULL ) {
1892
1913
/* Some arrays may refuse to export a buffer, in which case
1893
1914
* just fall back on regular __reduce_ex__ implementation
1894
1915
* (gh-12745).
1895
1916
*/
1917
+ Py_XDECREF (rev_perm );
1918
+ Py_XDECREF (transposed_array );
1896
1919
PyErr_Clear ();
1897
1920
return array_reduce_ex_regular (self , protocol );
1898
1921
}
1899
1922
1900
1923
/* Get the _frombuffer() function for reconstruction */
1901
-
1902
- numeric_mod = PyImport_ImportModule ("numpy._core.numeric" );
1903
- if (numeric_mod == NULL ) {
1924
+ if (npy_cache_import_runtime ("numpy._core.numeric" , "_frombuffer" ,
1925
+ & from_buffer_func ) == -1 ) {
1926
+ Py_XDECREF (rev_perm );
1927
+ Py_XDECREF (transposed_array );
1904
1928
Py_DECREF (buffer );
1905
1929
return NULL ;
1906
1930
}
1907
- from_buffer_func = PyObject_GetAttrString (numeric_mod ,
1908
- "_frombuffer" );
1909
- Py_DECREF (numeric_mod );
1910
- if (from_buffer_func == NULL ) {
1931
+
1932
+ PyObject * shape = NULL ;
1933
+ if (order == 'K' ) {
1934
+ shape = PyArray_IntTupleFromIntp (
1935
+ PyArray_NDIM ((PyArrayObject * )transposed_array ),
1936
+ PyArray_SHAPE ((PyArrayObject * )transposed_array ));
1937
+ }
1938
+ else {
1939
+ shape = PyArray_IntTupleFromIntp (PyArray_NDIM (self ),
1940
+ PyArray_SHAPE (self ));
1941
+ }
1942
+ Py_XDECREF (transposed_array );
1943
+ if (shape == NULL ) {
1944
+ Py_XDECREF (rev_perm );
1911
1945
Py_DECREF (buffer );
1912
1946
return NULL ;
1913
1947
}
1914
-
1915
- return Py_BuildValue ("N(NONN)" ,
1916
- from_buffer_func , buffer , (PyObject * )descr ,
1917
- PyObject_GetAttrString ((PyObject * )self , "shape" ),
1918
- PyUnicode_FromStringAndSize (& order , 1 ));
1948
+ if (order == 'K' ) {
1949
+ return Py_BuildValue ("N(NONNN)" , from_buffer_func , buffer ,
1950
+ (PyObject * )descr , shape ,
1951
+ PyUnicode_FromStringAndSize (& order , 1 ), rev_perm );
1952
+ }
1953
+ else {
1954
+ return Py_BuildValue ("N(NONN)" , from_buffer_func , buffer ,
1955
+ (PyObject * )descr , shape ,
1956
+ PyUnicode_FromStringAndSize (& order , 1 ));
1957
+ }
1919
1958
}
1920
1959
1921
1960
static PyObject *
@@ -1930,8 +1969,6 @@ array_reduce_ex(PyArrayObject *self, PyObject *args)
1930
1969
1931
1970
descr = PyArray_DESCR (self );
1932
1971
if ((protocol < 5 ) ||
1933
- (!PyArray_IS_C_CONTIGUOUS ((PyArrayObject * )self ) &&
1934
- !PyArray_IS_F_CONTIGUOUS ((PyArrayObject * )self )) ||
1935
1972
PyDataType_FLAGCHK (descr , NPY_ITEM_HASOBJECT ) ||
1936
1973
(PyType_IsSubtype (((PyObject * )self )-> ob_type , & PyArray_Type ) &&
1937
1974
((PyObject * )self )-> ob_type != & PyArray_Type ) ||
@@ -1943,6 +1980,11 @@ array_reduce_ex(PyArrayObject *self, PyObject *args)
1943
1980
return array_reduce_ex_regular (self , protocol );
1944
1981
}
1945
1982
else {
1983
+ /* The func will check internally
1984
+ * if the array isn't backed by a contiguous data buffer or
1985
+ * if the array refuses to export a buffer
1986
+ * In either case, fall back to `array_reduce_ex_regular`
1987
+ */
1946
1988
return array_reduce_ex_picklebuffer (self , protocol );
1947
1989
}
1948
1990
}
0 commit comments