From 5124891e662a1c2f703aad9cc75455096da17658 Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 14:46:25 +0800 Subject: [PATCH 01/20] Added public PyObject_CopyToObject() to pybuffer.h --- Include/pybuffer.h | 2 ++ ...4-09-29-14-34-53.gh-issue-84016.f1ThMN.rst | 1 + Objects/abstract.c | 34 +++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 Misc/NEWS.d/next/C_API/2024-09-29-14-34-53.gh-issue-84016.f1ThMN.rst diff --git a/Include/pybuffer.h b/Include/pybuffer.h index ca1c6058d9052c..d534cece44d4fd 100644 --- a/Include/pybuffer.h +++ b/Include/pybuffer.h @@ -75,6 +75,8 @@ PyAPI_FUNC(int) PyBuffer_FromContiguous(const Py_buffer *view, const void *buf, is 'A', then it does not matter and the copy will be made in whatever way is more efficient. */ PyAPI_FUNC(int) PyObject_CopyData(PyObject *dest, PyObject *src); +PyAPI_FUNC(int) PyObject_CopyToObject(PyObject *obj, void *buf, + Py_ssize_t len, char fortran); /* Copy the data from the src buffer to the buffer of destination. */ PyAPI_FUNC(int) PyBuffer_IsContiguous(const Py_buffer *view, char fort); diff --git a/Misc/NEWS.d/next/C_API/2024-09-29-14-34-53.gh-issue-84016.f1ThMN.rst b/Misc/NEWS.d/next/C_API/2024-09-29-14-34-53.gh-issue-84016.f1ThMN.rst new file mode 100644 index 00000000000000..0311d29d9cf78d --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-09-29-14-34-53.gh-issue-84016.f1ThMN.rst @@ -0,0 +1 @@ +Added public :c:func:`PyObject_CopyToObject()` to ``pybuffer.h`` diff --git a/Objects/abstract.c b/Objects/abstract.c index 7cca81464cd112..02caa44f6ad832 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -733,6 +733,40 @@ int PyObject_CopyData(PyObject *dest, PyObject *src) return 0; } +int +PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) +{ + Py_buffer view_obj; + + if (!PyObject_CheckBuffer(obj)) { + PyErr_SetString(PyExc_TypeError, + "object doesn't support buffer"); + return -1; + } + + if (PyObject_GetBuffer(obj, &view_obj, PyBUF_SIMPLE)) { + return -1; + } + + if (view_obj.len < len) { + PyErr_SetString(PyExc_BufferError, + "buffer length is insufficient"); + PyBuffer_Release(&view_obj); + return -1; + } + + /* just copy it directly through memcpy */ + if (fort == 'C' || fort == 'F') { + memcpy(view_obj.buf, buf, len); + PyBuffer_Release(&view_obj); + return 0; + } + + /* i'm thinking... */ + + return -1; +} + void PyBuffer_FillContiguousStrides(int nd, Py_ssize_t *shape, Py_ssize_t *strides, int itemsize, From 686673e5c18011ff0b3db899cfe8dd827c699a9b Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 15:03:12 +0800 Subject: [PATCH 02/20] int \n change to int --- Objects/abstract.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index 02caa44f6ad832..712b96fc28984a 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -733,8 +733,7 @@ int PyObject_CopyData(PyObject *dest, PyObject *src) return 0; } -int -PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) +int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) { Py_buffer view_obj; @@ -761,7 +760,6 @@ PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) PyBuffer_Release(&view_obj); return 0; } - /* i'm thinking... */ return -1; From 2eff13dd7bea226541fe4523d375624db66197d2 Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 15:07:54 +0800 Subject: [PATCH 03/20] Change error string --- Objects/abstract.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index 712b96fc28984a..c511144d7711ab 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -739,7 +739,7 @@ int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) if (!PyObject_CheckBuffer(obj)) { PyErr_SetString(PyExc_TypeError, - "object doesn't support buffer"); + "destination must be bytes-like objects"); return -1; } @@ -749,7 +749,8 @@ int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) if (view_obj.len < len) { PyErr_SetString(PyExc_BufferError, - "buffer length is insufficient"); + "destination is too small to" + "receive data from buf"); PyBuffer_Release(&view_obj); return -1; } From 4569791765319420f52578bcf39c8b49d283f36a Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 15:09:01 +0800 Subject: [PATCH 04/20] fort rename to fortran --- Objects/abstract.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index c511144d7711ab..e7075bbcce1ce4 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -733,7 +733,7 @@ int PyObject_CopyData(PyObject *dest, PyObject *src) return 0; } -int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) +int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fortran) { Py_buffer view_obj; @@ -756,7 +756,7 @@ int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) } /* just copy it directly through memcpy */ - if (fort == 'C' || fort == 'F') { + if (fortran == 'C' || fortran == 'F') { memcpy(view_obj.buf, buf, len); PyBuffer_Release(&view_obj); return 0; From a91c5eab8a7bf08f2e95df7b75f73a82641dfc0e Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 15:30:59 +0800 Subject: [PATCH 05/20] Add document --- Doc/c-api/buffer.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 9500fe465c7d94..3abde1c0b8e2e2 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -517,6 +517,15 @@ Buffer-related functions ``0`` is returned on success, ``-1`` on error. +.. c:function:: int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fortran) + + Copy data from *buf* to *obj* buffer. Can convert between C-style and + or Fortran-style buffers. + + ``0`` is returned on success, ``-1`` on error. + + .. versionadded:: 3.14 + .. c:function:: void PyBuffer_FillContiguousStrides(int ndims, Py_ssize_t *shape, Py_ssize_t *strides, int itemsize, char order) Fill the *strides* array with byte-strides of a :term:`contiguous` (C-style if From df16015501447ccd7ddbe7cec5c6c678432f117e Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 17:31:08 +0800 Subject: [PATCH 06/20] Remove useless code --- Include/pybuffer.h | 2 +- Objects/abstract.c | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Include/pybuffer.h b/Include/pybuffer.h index d534cece44d4fd..e33958a3a9d03b 100644 --- a/Include/pybuffer.h +++ b/Include/pybuffer.h @@ -76,7 +76,7 @@ PyAPI_FUNC(int) PyBuffer_FromContiguous(const Py_buffer *view, const void *buf, in whatever way is more efficient. */ PyAPI_FUNC(int) PyObject_CopyData(PyObject *dest, PyObject *src); PyAPI_FUNC(int) PyObject_CopyToObject(PyObject *obj, void *buf, - Py_ssize_t len, char fortran); + Py_ssize_t len, char fort); /* Copy the data from the src buffer to the buffer of destination. */ PyAPI_FUNC(int) PyBuffer_IsContiguous(const Py_buffer *view, char fort); diff --git a/Objects/abstract.c b/Objects/abstract.c index e7075bbcce1ce4..9182db5573b658 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -663,7 +663,8 @@ PyBuffer_FromContiguous(const Py_buffer *view, const void *buf, Py_ssize_t len, return 0; } -int PyObject_CopyData(PyObject *dest, PyObject *src) +int +PyObject_CopyData(PyObject *dest, PyObject *src) { Py_buffer view_dest, view_src; int k; @@ -733,7 +734,8 @@ int PyObject_CopyData(PyObject *dest, PyObject *src) return 0; } -int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fortran) +int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, + char Py_UNUSED(fort)) { Py_buffer view_obj; @@ -743,6 +745,11 @@ int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fortran return -1; } + if (!buf) { + PyErr_SetString(PyExc_MemoryError, "buf pointer is null"); + return -1; + } + if (PyObject_GetBuffer(obj, &view_obj, PyBUF_SIMPLE)) { return -1; } @@ -756,14 +763,9 @@ int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fortran } /* just copy it directly through memcpy */ - if (fortran == 'C' || fortran == 'F') { - memcpy(view_obj.buf, buf, len); - PyBuffer_Release(&view_obj); - return 0; - } - /* i'm thinking... */ - - return -1; + memcpy(view_obj.buf, buf, len); + PyBuffer_Release(&view_obj); + return 0; } void From 50bfb4281f86226195c18dd705a62aee7a3b0177 Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 17:31:53 +0800 Subject: [PATCH 07/20] Add \n --- Objects/abstract.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index 9182db5573b658..196f18961c4b1f 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -734,7 +734,8 @@ PyObject_CopyData(PyObject *dest, PyObject *src) return 0; } -int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, +int +PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char Py_UNUSED(fort)) { Py_buffer view_obj; From 6498381fc2efb6fe874f785913a3f37ac1763fbb Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 17:35:10 +0800 Subject: [PATCH 08/20] Remove space --- Objects/abstract.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index 196f18961c4b1f..27127abe813593 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -663,7 +663,7 @@ PyBuffer_FromContiguous(const Py_buffer *view, const void *buf, Py_ssize_t len, return 0; } -int +int PyObject_CopyData(PyObject *dest, PyObject *src) { Py_buffer view_dest, view_src; From 3c0436e838918ff76352c8f9c57c94bb7b8b55a5 Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 17:37:34 +0800 Subject: [PATCH 09/20] Change style --- Objects/abstract.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index 27127abe813593..8450acabcbf238 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -736,7 +736,7 @@ PyObject_CopyData(PyObject *dest, PyObject *src) int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, - char Py_UNUSED(fort)) + char Py_UNUSED(fort)) { Py_buffer view_obj; From 0e7de15c6a3dbfea6d1d0dd915afe84ba50f0d3d Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 21:07:04 +0800 Subject: [PATCH 10/20] Change NEWS --- .../next/C_API/2024-09-29-14-34-53.gh-issue-84016.f1ThMN.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/C_API/2024-09-29-14-34-53.gh-issue-84016.f1ThMN.rst b/Misc/NEWS.d/next/C_API/2024-09-29-14-34-53.gh-issue-84016.f1ThMN.rst index 0311d29d9cf78d..a64fd6851f204d 100644 --- a/Misc/NEWS.d/next/C_API/2024-09-29-14-34-53.gh-issue-84016.f1ThMN.rst +++ b/Misc/NEWS.d/next/C_API/2024-09-29-14-34-53.gh-issue-84016.f1ThMN.rst @@ -1 +1,2 @@ -Added public :c:func:`PyObject_CopyToObject()` to ``pybuffer.h`` +Added :c:func:`PyObject_CopyToObject` to copy a C-style or Fortran-style +buffer to a Python object. From 1d797ef2df1486c541243147eb488b53f053baca Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 21:12:22 +0800 Subject: [PATCH 11/20] Change review --- Objects/abstract.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index 8450acabcbf238..b73b97b75ada48 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -746,19 +746,13 @@ PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, return -1; } - if (!buf) { - PyErr_SetString(PyExc_MemoryError, "buf pointer is null"); - return -1; - } - if (PyObject_GetBuffer(obj, &view_obj, PyBUF_SIMPLE)) { return -1; } if (view_obj.len < len) { PyErr_SetString(PyExc_BufferError, - "destination is too small to" - "receive data from buf"); + "destination is too small to receive data from buf"); PyBuffer_Release(&view_obj); return -1; } From 3388efff6b199d50b1860453bfc9c4018a4c2d79 Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 21:32:40 +0800 Subject: [PATCH 12/20] Change error string --- Objects/abstract.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index b73b97b75ada48..206556767cf9a3 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -742,7 +742,7 @@ PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, if (!PyObject_CheckBuffer(obj)) { PyErr_SetString(PyExc_TypeError, - "destination must be bytes-like objects"); + "object supporting the buffer protocol required"); return -1; } From 6e9bfd819cb1b755c9a3e45033593a75dd90fff0 Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 22:55:43 +0800 Subject: [PATCH 13/20] Change doc --- Doc/c-api/buffer.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 3abde1c0b8e2e2..11ddc9ed2c5da9 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -517,10 +517,11 @@ Buffer-related functions ``0`` is returned on success, ``-1`` on error. -.. c:function:: int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fortran) +.. c:function:: int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) Copy data from *buf* to *obj* buffer. Can convert between C-style and - or Fortran-style buffers. + or Fortran-style buffers (C-style if *fort* is ``'C'`` or Fortran-style + if *fort* is ``'F'``, ``'A'``or other for defalut quick copy). ``0`` is returned on success, ``-1`` on error. From e83402ace57a6e75c632d02450ff84e2f59853fb Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 23:52:56 +0800 Subject: [PATCH 14/20] Re implement PyObject_CopyToObject --- Objects/abstract.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index 206556767cf9a3..147c3e8dfc6a3a 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -735,11 +735,13 @@ PyObject_CopyData(PyObject *dest, PyObject *src) } int -PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, - char Py_UNUSED(fort)) +PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) { + char *tmp_obj, *tmp_buf = buf; Py_buffer view_obj; + Py_ssize_t *indices, elem_num = 1; + assert(buf == NULL); if (!PyObject_CheckBuffer(obj)) { PyErr_SetString(PyExc_TypeError, "object supporting the buffer protocol required"); @@ -758,7 +760,31 @@ PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, } /* just copy it directly through memcpy */ - memcpy(view_obj.buf, buf, len); + if (fort == 'C' || fort == 'F') { + memcpy(view_obj.buf, buf, len); + PyBuffer_Release(&view_obj); + return 0; + } + + /* quick copy implementation */ + indices = (Py_ssize_t *)PyMem_Malloc(sizeof(Py_ssize_t) * view_obj.ndim); + if (!indices) { + PyErr_NoMemory(); + PyBuffer_Release(&view_obj); + return -1; + } + memset(indices, 0, view_obj.ndim); + for (int i = 0; i < view_obj.ndim; i++) { + /* XXX(nnorwitz): can this overflow? */ + elem_num *= view_obj.shape[i]; + } + while (elem_num--) { + tmp_obj = PyBuffer_GetPointer(&view_obj, indices); + memcpy(tmp_obj, tmp_buf, view_obj.itemsize); + tmp_buf += view_obj.itemsize; + _Py_add_one_to_index_C(view_obj.ndim, indices, view_obj.shape); + } + PyMem_Free(indices); PyBuffer_Release(&view_obj); return 0; } From 981b6c515e7d5b9ac922e8706d3c2379086c5dd4 Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 29 Sep 2024 23:55:33 +0800 Subject: [PATCH 15/20] Change doc --- Doc/c-api/buffer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 11ddc9ed2c5da9..6285370e9ee493 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -521,7 +521,7 @@ Buffer-related functions Copy data from *buf* to *obj* buffer. Can convert between C-style and or Fortran-style buffers (C-style if *fort* is ``'C'`` or Fortran-style - if *fort* is ``'F'``, ``'A'``or other for defalut quick copy). + if *fort* is ``'F'``, ``'A'`` or other for defalut quick copy). ``0`` is returned on success, ``-1`` on error. From 7bc039ec188d148187be83e5486490e9b7cc8dd7 Mon Sep 17 00:00:00 2001 From: RUANG Date: Mon, 30 Sep 2024 09:18:18 +0800 Subject: [PATCH 16/20] Regen stable_abi --- Doc/data/stable_abi.dat | 1 + Lib/test/test_stable_abi_ctypes.py | 1 + Misc/stable_abi.toml | 2 ++ PC/python3dll.c | 1 + 4 files changed, 5 insertions(+) diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 7eeee270bb7f32..4f34d45e91cc5b 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -518,6 +518,7 @@ func,PyObject_Calloc,3.7,, func,PyObject_CheckBuffer,3.11,, func,PyObject_ClearWeakRefs,3.2,, func,PyObject_CopyData,3.11,, +func,PyObject_CopyToObject,3.14,, func,PyObject_DelAttr,3.13,, func,PyObject_DelAttrString,3.13,, func,PyObject_DelItem,3.2,, diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index 4bca33b7451f80..93ec46ca7fe5d9 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -546,6 +546,7 @@ def test_windows_feature_macros(self): "PyObject_CheckReadBuffer", "PyObject_ClearWeakRefs", "PyObject_CopyData", + "PyObject_CopyToObject", "PyObject_DelAttr", "PyObject_DelAttrString", "PyObject_DelItem", diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 8bf638c473c712..c991fc620fdc1f 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2283,6 +2283,8 @@ added = '3.11' [function.PyObject_CopyData] added = '3.11' +[function.PyObject_CopyToObject] + added = '3.14' [function.PyBuffer_IsContiguous] added = '3.11' [function.PyBuffer_FillContiguousStrides] diff --git a/PC/python3dll.c b/PC/python3dll.c index 1845334b244d8c..503cf706b71bb7 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -477,6 +477,7 @@ EXPORT_FUNC(PyObject_CheckBuffer) EXPORT_FUNC(PyObject_CheckReadBuffer) EXPORT_FUNC(PyObject_ClearWeakRefs) EXPORT_FUNC(PyObject_CopyData) +EXPORT_FUNC(PyObject_CopyToObject) EXPORT_FUNC(PyObject_DelAttr) EXPORT_FUNC(PyObject_DelAttrString) EXPORT_FUNC(PyObject_DelItem) From edd093d0df80cabbacbde4804dc36dd6e9621ce2 Mon Sep 17 00:00:00 2001 From: ruang Date: Tue, 1 Oct 2024 13:30:51 +0800 Subject: [PATCH 17/20] Change PyObject_CopyToObject --- Objects/abstract.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index 147c3e8dfc6a3a..bc7d62ec047d74 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -748,7 +748,7 @@ PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) return -1; } - if (PyObject_GetBuffer(obj, &view_obj, PyBUF_SIMPLE)) { + if (PyObject_GetBuffer(obj, &view_obj, PyBUF_FULL_RO)) { return -1; } @@ -767,21 +767,19 @@ PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) } /* quick copy implementation */ - indices = (Py_ssize_t *)PyMem_Malloc(sizeof(Py_ssize_t) * view_obj.ndim); + indices = (Py_ssize_t *)PyMem_Calloc(view_obj.ndim, sizeof(Py_ssize_t)); if (!indices) { PyErr_NoMemory(); PyBuffer_Release(&view_obj); return -1; } - memset(indices, 0, view_obj.ndim); for (int i = 0; i < view_obj.ndim; i++) { /* XXX(nnorwitz): can this overflow? */ elem_num *= view_obj.shape[i]; } while (elem_num--) { tmp_obj = PyBuffer_GetPointer(&view_obj, indices); - memcpy(tmp_obj, tmp_buf, view_obj.itemsize); - tmp_buf += view_obj.itemsize; + memcpy(tmp_obj, tmp_buf++, view_obj.itemsize); _Py_add_one_to_index_C(view_obj.ndim, indices, view_obj.shape); } PyMem_Free(indices); From 611ad22985fff6db6dc36df2b415ac6561894357 Mon Sep 17 00:00:00 2001 From: ruang Date: Tue, 1 Oct 2024 18:28:43 +0800 Subject: [PATCH 18/20] Add test --- Lib/test/test_capi/test_abstract.py | 14 ++++++++++++++ Modules/_testcapi/abstract.c | 21 +++++++++++++++++++++ Objects/abstract.c | 2 +- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_capi/test_abstract.py b/Lib/test/test_capi/test_abstract.py index 6a626813f23379..d102190b301d2a 100644 --- a/Lib/test/test_capi/test_abstract.py +++ b/Lib/test/test_capi/test_abstract.py @@ -339,6 +339,20 @@ def test_object_delattrstring(self): # CRASHES delattrstring(obj, NULL) # CRASHES delattrstring(NULL, b'a') + def test_copy_to_object(self): + copy_to_object = _testcapi.object_copy_to_object + s1 = copy_to_object(bytes(3), 'abc', 'C') + s2 = copy_to_object(bytes(3), 'abc', 'F') + s3 = copy_to_object(bytes(3), 'abc', 'A') + self.assertEqual(s1, s2) + self.assertEqual(s2, s3) + self.assertEqual(s1, s3) + self.assertRaises(BufferError, copy_to_object, bytes(2), 'abc', 'C') + self.assertRaises(BufferError, copy_to_object, bytes(2), 'abc', 'F') + self.assertRaises(BufferError, copy_to_object, bytes(2), 'abc', 'A') + self.assertRaises(TypeError, copy_to_object, list(), 'abc', 'C') + self.assertRaises(TypeError, copy_to_object, list(), 'abc', 'F') + self.assertRaises(TypeError, copy_to_object, list(), 'abc', 'A') def test_mapping_check(self): check = _testlimitedcapi.mapping_check diff --git a/Modules/_testcapi/abstract.c b/Modules/_testcapi/abstract.c index 8c2c7137cdce40..05442a56903990 100644 --- a/Modules/_testcapi/abstract.c +++ b/Modules/_testcapi/abstract.c @@ -78,6 +78,25 @@ object_hasattrstringwitherror(PyObject *self, PyObject *args) RETURN_INT(PyObject_HasAttrStringWithError(obj, attr_name)); } +static PyObject * +object_copy_to_object(PyObject *self, PyObject *args) +{ + PyObject *obj; + Py_ssize_t len; + int result; + char *buf, fort; + + if (!PyArg_ParseTuple(args, "Os#C", &obj, &buf, &len, &fort)) { + return NULL; + } + result = PyObject_CopyToObject(obj, buf, len, fort); + if (result < 0) { + return NULL; + } + Py_INCREF(obj); + return obj; +} + static PyObject * mapping_getoptionalitemstring(PyObject *self, PyObject *args) { @@ -162,6 +181,8 @@ static PyMethodDef test_methods[] = { {"object_getoptionalattrstring", object_getoptionalattrstring, METH_VARARGS}, {"object_hasattrwitherror", object_hasattrwitherror, METH_VARARGS}, {"object_hasattrstringwitherror", object_hasattrstringwitherror, METH_VARARGS}, + {"object_copy_to_object", object_copy_to_object, METH_VARARGS}, + {"mapping_getoptionalitem", mapping_getoptionalitem, METH_VARARGS}, {"mapping_getoptionalitemstring", mapping_getoptionalitemstring, METH_VARARGS}, diff --git a/Objects/abstract.c b/Objects/abstract.c index bc7d62ec047d74..5207810e5284fb 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -741,7 +741,7 @@ PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) Py_buffer view_obj; Py_ssize_t *indices, elem_num = 1; - assert(buf == NULL); + assert(buf != NULL); if (!PyObject_CheckBuffer(obj)) { PyErr_SetString(PyExc_TypeError, "object supporting the buffer protocol required"); From eb1a891c531e9dbdd9d8d432c0756b638ed33aec Mon Sep 17 00:00:00 2001 From: ruang Date: Wed, 2 Oct 2024 09:43:23 +0800 Subject: [PATCH 19/20] Change to bytearray --- Lib/test/test_capi/test_abstract.py | 12 ++++++------ Objects/abstract.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_capi/test_abstract.py b/Lib/test/test_capi/test_abstract.py index d102190b301d2a..209e7332325e4f 100644 --- a/Lib/test/test_capi/test_abstract.py +++ b/Lib/test/test_capi/test_abstract.py @@ -341,15 +341,15 @@ def test_object_delattrstring(self): def test_copy_to_object(self): copy_to_object = _testcapi.object_copy_to_object - s1 = copy_to_object(bytes(3), 'abc', 'C') - s2 = copy_to_object(bytes(3), 'abc', 'F') - s3 = copy_to_object(bytes(3), 'abc', 'A') + s1 = copy_to_object(bytearray(3), 'abc', 'C') + s2 = copy_to_object(bytearray(3), 'abc', 'F') + s3 = copy_to_object(bytearray(3), 'abc', 'A') self.assertEqual(s1, s2) self.assertEqual(s2, s3) self.assertEqual(s1, s3) - self.assertRaises(BufferError, copy_to_object, bytes(2), 'abc', 'C') - self.assertRaises(BufferError, copy_to_object, bytes(2), 'abc', 'F') - self.assertRaises(BufferError, copy_to_object, bytes(2), 'abc', 'A') + self.assertRaises(BufferError, copy_to_object, bytearray(2), 'abc', 'C') + self.assertRaises(BufferError, copy_to_object, bytearray(2), 'abc', 'F') + self.assertRaises(BufferError, copy_to_object, bytearray(2), 'abc', 'A') self.assertRaises(TypeError, copy_to_object, list(), 'abc', 'C') self.assertRaises(TypeError, copy_to_object, list(), 'abc', 'F') self.assertRaises(TypeError, copy_to_object, list(), 'abc', 'A') diff --git a/Objects/abstract.c b/Objects/abstract.c index 5207810e5284fb..0c04cca33134d8 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -748,7 +748,7 @@ PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, char fort) return -1; } - if (PyObject_GetBuffer(obj, &view_obj, PyBUF_FULL_RO)) { + if (PyObject_GetBuffer(obj, &view_obj, PyBUF_FULL)) { return -1; } From 11532756d79404a3c458b1c6d024f0ec04db395f Mon Sep 17 00:00:00 2001 From: ruang Date: Wed, 2 Oct 2024 13:38:47 +0800 Subject: [PATCH 20/20] Change test --- Lib/test/test_capi/test_abstract.py | 18 +++++++++--------- Modules/_testcapi/abstract.c | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_capi/test_abstract.py b/Lib/test/test_capi/test_abstract.py index 209e7332325e4f..c2014628b64115 100644 --- a/Lib/test/test_capi/test_abstract.py +++ b/Lib/test/test_capi/test_abstract.py @@ -341,18 +341,18 @@ def test_object_delattrstring(self): def test_copy_to_object(self): copy_to_object = _testcapi.object_copy_to_object - s1 = copy_to_object(bytearray(3), 'abc', 'C') - s2 = copy_to_object(bytearray(3), 'abc', 'F') - s3 = copy_to_object(bytearray(3), 'abc', 'A') + s1 = copy_to_object(bytearray(3), 'abc', b'C') + s2 = copy_to_object(bytearray(3), 'abc', b'F') + s3 = copy_to_object(bytearray(3), 'abc', b'A') self.assertEqual(s1, s2) self.assertEqual(s2, s3) self.assertEqual(s1, s3) - self.assertRaises(BufferError, copy_to_object, bytearray(2), 'abc', 'C') - self.assertRaises(BufferError, copy_to_object, bytearray(2), 'abc', 'F') - self.assertRaises(BufferError, copy_to_object, bytearray(2), 'abc', 'A') - self.assertRaises(TypeError, copy_to_object, list(), 'abc', 'C') - self.assertRaises(TypeError, copy_to_object, list(), 'abc', 'F') - self.assertRaises(TypeError, copy_to_object, list(), 'abc', 'A') + self.assertRaises(BufferError, copy_to_object, bytearray(2), 'abc', b'C') + self.assertRaises(BufferError, copy_to_object, bytearray(2), 'abc', b'F') + self.assertRaises(BufferError, copy_to_object, bytearray(2), 'abc', b'A') + self.assertRaises(TypeError, copy_to_object, list(), 'abc', b'C') + self.assertRaises(TypeError, copy_to_object, list(), 'abc', b'F') + self.assertRaises(TypeError, copy_to_object, list(), 'abc', b'A') def test_mapping_check(self): check = _testlimitedcapi.mapping_check diff --git a/Modules/_testcapi/abstract.c b/Modules/_testcapi/abstract.c index 05442a56903990..e8be80c3f5bd0c 100644 --- a/Modules/_testcapi/abstract.c +++ b/Modules/_testcapi/abstract.c @@ -86,7 +86,7 @@ object_copy_to_object(PyObject *self, PyObject *args) int result; char *buf, fort; - if (!PyArg_ParseTuple(args, "Os#C", &obj, &buf, &len, &fort)) { + if (!PyArg_ParseTuple(args, "Os#c", &obj, &buf, &len, &fort)) { return NULL; } result = PyObject_CopyToObject(obj, buf, len, fort);