bpo-41625: Expose the splice() system call in the os module (GH-21947)

This commit is contained in:
Pablo Galindo 2020-11-17 00:00:38 +00:00 committed by GitHub
parent cce3f0b0c8
commit a57b3d30f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 349 additions and 78 deletions

View file

@ -5674,6 +5674,106 @@ exit:
#endif /* defined(HAVE_COPY_FILE_RANGE) */
#if defined(HAVE_SPLICE)
PyDoc_STRVAR(os_splice__doc__,
"splice($module, /, src, dst, count, offset_src=None, offset_dst=None,\n"
" flags=0)\n"
"--\n"
"\n"
"Transfer count bytes from one pipe to a descriptor or vice versa.\n"
"\n"
" src\n"
" Source file descriptor.\n"
" dst\n"
" Destination file descriptor.\n"
" count\n"
" Number of bytes to copy.\n"
" offset_src\n"
" Starting offset in src.\n"
" offset_dst\n"
" Starting offset in dst.\n"
" flags\n"
" Flags to modify the semantics of the call.\n"
"\n"
"If offset_src is None, then src is read from the current position;\n"
"respectively for offset_dst. The offset associated to the file\n"
"descriptor that refers to a pipe must be None.");
#define OS_SPLICE_METHODDEF \
{"splice", (PyCFunction)(void(*)(void))os_splice, METH_FASTCALL|METH_KEYWORDS, os_splice__doc__},
static PyObject *
os_splice_impl(PyObject *module, int src, int dst, Py_ssize_t count,
PyObject *offset_src, PyObject *offset_dst,
unsigned int flags);
static PyObject *
os_splice(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
static const char * const _keywords[] = {"src", "dst", "count", "offset_src", "offset_dst", "flags", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "splice", 0};
PyObject *argsbuf[6];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 3;
int src;
int dst;
Py_ssize_t count;
PyObject *offset_src = Py_None;
PyObject *offset_dst = Py_None;
unsigned int flags = 0;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 6, 0, argsbuf);
if (!args) {
goto exit;
}
src = _PyLong_AsInt(args[0]);
if (src == -1 && PyErr_Occurred()) {
goto exit;
}
dst = _PyLong_AsInt(args[1]);
if (dst == -1 && PyErr_Occurred()) {
goto exit;
}
{
Py_ssize_t ival = -1;
PyObject *iobj = _PyNumber_Index(args[2]);
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
}
if (ival == -1 && PyErr_Occurred()) {
goto exit;
}
count = ival;
}
if (!noptargs) {
goto skip_optional_pos;
}
if (args[3]) {
offset_src = args[3];
if (!--noptargs) {
goto skip_optional_pos;
}
}
if (args[4]) {
offset_dst = args[4];
if (!--noptargs) {
goto skip_optional_pos;
}
}
if (!_PyLong_UnsignedInt_Converter(args[5], &flags)) {
goto exit;
}
skip_optional_pos:
return_value = os_splice_impl(module, src, dst, count, offset_src, offset_dst, flags);
exit:
return return_value;
}
#endif /* defined(HAVE_SPLICE) */
#if defined(HAVE_MKFIFO)
PyDoc_STRVAR(os_mkfifo__doc__,
@ -8864,6 +8964,10 @@ exit:
#define OS_COPY_FILE_RANGE_METHODDEF
#endif /* !defined(OS_COPY_FILE_RANGE_METHODDEF) */
#ifndef OS_SPLICE_METHODDEF
#define OS_SPLICE_METHODDEF
#endif /* !defined(OS_SPLICE_METHODDEF) */
#ifndef OS_MKFIFO_METHODDEF
#define OS_MKFIFO_METHODDEF
#endif /* !defined(OS_MKFIFO_METHODDEF) */
@ -9059,4 +9163,4 @@ exit:
#ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF
#define OS_WAITSTATUS_TO_EXITCODE_METHODDEF
#endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */
/*[clinic end generated code: output=49b7ed768242ef7c input=a9049054013a1b77]*/
/*[clinic end generated code: output=8a59e91178897267 input=a9049054013a1b77]*/

View file

@ -6521,7 +6521,6 @@ os_spawnve_impl(PyObject *module, int mode, path_t *path, PyObject *argv,
#endif /* HAVE_SPAWNV */
#ifdef HAVE_FORK
/* Helper function to validate arguments.
@ -10370,6 +10369,75 @@ os_copy_file_range_impl(PyObject *module, int src, int dst, Py_ssize_t count,
}
#endif /* HAVE_COPY_FILE_RANGE*/
#ifdef HAVE_SPLICE
/*[clinic input]
os.splice
src: int
Source file descriptor.
dst: int
Destination file descriptor.
count: Py_ssize_t
Number of bytes to copy.
offset_src: object = None
Starting offset in src.
offset_dst: object = None
Starting offset in dst.
flags: unsigned_int = 0
Flags to modify the semantics of the call.
Transfer count bytes from one pipe to a descriptor or vice versa.
If offset_src is None, then src is read from the current position;
respectively for offset_dst. The offset associated to the file
descriptor that refers to a pipe must be None.
[clinic start generated code]*/
static PyObject *
os_splice_impl(PyObject *module, int src, int dst, Py_ssize_t count,
PyObject *offset_src, PyObject *offset_dst,
unsigned int flags)
/*[clinic end generated code: output=d0386f25a8519dc5 input=047527c66c6d2e0a]*/
{
off_t offset_src_val, offset_dst_val;
off_t *p_offset_src = NULL;
off_t *p_offset_dst = NULL;
Py_ssize_t ret;
int async_err = 0;
if (count < 0) {
PyErr_SetString(PyExc_ValueError, "negative value for 'count' not allowed");
return NULL;
}
if (offset_src != Py_None) {
if (!Py_off_t_converter(offset_src, &offset_src_val)) {
return NULL;
}
p_offset_src = &offset_src_val;
}
if (offset_dst != Py_None) {
if (!Py_off_t_converter(offset_dst, &offset_dst_val)) {
return NULL;
}
p_offset_dst = &offset_dst_val;
}
do {
Py_BEGIN_ALLOW_THREADS
ret = splice(src, p_offset_src, dst, p_offset_dst, count, flags);
Py_END_ALLOW_THREADS
} while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (ret < 0) {
return (!async_err) ? posix_error() : NULL;
}
return PyLong_FromSsize_t(ret);
}
#endif /* HAVE_SPLICE*/
#ifdef HAVE_MKFIFO
/*[clinic input]
os.mkfifo
@ -14550,6 +14618,7 @@ static PyMethodDef posix_methods[] = {
OS_POSIX_SPAWNP_METHODDEF
OS_READLINK_METHODDEF
OS_COPY_FILE_RANGE_METHODDEF
OS_SPLICE_METHODDEF
OS_RENAME_METHODDEF
OS_REPLACE_METHODDEF
OS_RMDIR_METHODDEF
@ -15072,6 +15141,13 @@ all_ins(PyObject *m)
if (PyModule_AddIntConstant(m, "RWF_APPEND", RWF_APPEND)) return -1;
#endif
/* constants for splice */
#ifdef HAVE_SPLICE
if (PyModule_AddIntConstant(m, "SPLICE_F_MOVE", SPLICE_F_MOVE)) return -1;
if (PyModule_AddIntConstant(m, "SPLICE_F_NONBLOCK", SPLICE_F_NONBLOCK)) return -1;
if (PyModule_AddIntConstant(m, "SPLICE_F_MORE", SPLICE_F_MORE)) return -1;
#endif
/* constants for posix_spawn */
#ifdef HAVE_POSIX_SPAWN
if (PyModule_AddIntConstant(m, "POSIX_SPAWN_OPEN", POSIX_SPAWN_OPEN)) return -1;