diff options
author | Adrian Herrmann <adrian.herrmann@qt.io> | 2023-08-30 19:05:38 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-08-31 12:27:08 +0000 |
commit | 7252c524eeba7d4e15100c1a5f1c717d17e9fe23 (patch) | |
tree | 4a91b98823e1a49951a76b2cba51c922164d9b3d | |
parent | 50676dae43c0e2f1fd4a2eedeb1a120054d8f3be (diff) |
QIODevice: Implement buffered reads
Some users of QIODevice read functions (read, readLine and peek) might
want to use fixed buffers to avoid reallocation, e.g., asyncio's
buffered protocols. This adds overloads of said read functions that take
an input buffer (as a bytearray) and return the number of read bytes.
Change-Id: I0c3678d3a87811029278c5ae8f829eef0432099a
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: Shyamnath Premnadh <Shyamnath.Premnadh@qt.io>
(cherry picked from commit 762cffbd319834245e8994f3104fc722e1394bd2)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | sources/pyside6/PySide6/QtCore/typesystem_core_common.xml | 15 | ||||
-rw-r--r-- | sources/pyside6/PySide6/glue/qtcore.cpp | 7 | ||||
-rw-r--r-- | sources/pyside6/tests/QtCore/qiodevice_buffered_read_test.py | 78 |
3 files changed, 97 insertions, 3 deletions
diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml index 153de1bf2..82089db67 100644 --- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml +++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml @@ -2287,12 +2287,21 @@ <modify-function signature="write(const QByteArray&)" allow-thread="yes"/> <modify-function signature="waitForReadyRead(int)" allow-thread="yes"/> <modify-function signature="waitForBytesWritten(int)" allow-thread="yes"/> - <!-- ### peek(qint64) do the job --> <modify-function signature="peek(char*,qint64)" remove="all"/> - <!-- ### read(qint64) do the job --> + <add-function signature="peek(PyBuffer@buffer@,qint64@maxlen@)" return-type="qint64"> + <modify-argument index="1" pyi-type="bytearray"/> + <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qiodevice-bufferedread"/> + </add-function> <modify-function signature="read(char*,qint64)" remove="all"/> - <!-- ### readLine(qint64) do the job --> + <add-function signature="read(PyBuffer@buffer@,qint64@maxlen@)" return-type="qint64"> + <modify-argument index="1" pyi-type="bytearray"/> + <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qiodevice-bufferedread"/> + </add-function> <modify-function signature="readLine(char*,qint64)" remove="all"/> + <add-function signature="readLine(PyBuffer@buffer@,qint64@maxlen@)" return-type="qint64"> + <modify-argument index="1" pyi-type="bytearray"/> + <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qiodevice-bufferedread"/> + </add-function> <!-- ### write(str) do the job --> <modify-function signature="write(const char*,qint64)" remove="all"/> <modify-function signature="write(const char*)" remove="all"/> diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp index 80aaca96a..97b711fe7 100644 --- a/sources/pyside6/PySide6/glue/qtcore.cpp +++ b/sources/pyside6/PySide6/glue/qtcore.cpp @@ -946,6 +946,13 @@ uchar *ptr = reinterpret_cast<uchar *>(Shiboken::Buffer::getPointer(%PYARG_1)); %PYARG_0 = Shiboken::Buffer::newObject(%CPPSELF.%FUNCTION_NAME(%1, %2, %3), %2, Shiboken::Buffer::ReadWrite); // @snippet qfiledevice-map +// @snippet qiodevice-bufferedread +Py_ssize_t bufferLen; +auto *data = reinterpret_cast<char*>(Shiboken::Buffer::getPointer(%PYARG_1, &bufferLen)); +%RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(data, PyLong_AsLongLong(%PYARG_2)); +return PyLong_FromLong(%0); +// @snippet qiodevice-bufferedread + // @snippet qiodevice-readdata QByteArray ba(1 + qsizetype(%2), char(0)); %CPPSELF.%FUNCTION_NAME(ba.data(), qint64(%2)); diff --git a/sources/pyside6/tests/QtCore/qiodevice_buffered_read_test.py b/sources/pyside6/tests/QtCore/qiodevice_buffered_read_test.py new file mode 100644 index 000000000..ea735112a --- /dev/null +++ b/sources/pyside6/tests/QtCore/qiodevice_buffered_read_test.py @@ -0,0 +1,78 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for buffered read methods of QIODevice''' + +from PySide6.QtCore import QBuffer + +import enum +import unittest + + +class TestQIODeviceBufferedRead(unittest.TestCase): + class TestType(enum.Enum): + Read = enum.auto() + ReadLine = enum.auto() + Peek = enum.auto() + + def setUp(self) -> None: + self.buffer = QBuffer() + self.text = "Tomato juice\nPotato salad\n" + + self.assertTrue( + self.buffer.open(QBuffer.OpenModeFlag.ReadWrite), self.buffer.errorString()) + self.assertGreaterEqual( + self.buffer.write(self.text.encode("utf-8")), 0, self.buffer.errorString()) + + self.buffer.seek(0) + + def tearDown(self) -> None: + self.buffer.close() + + def test_read(self) -> None: + response1 = self.buffer.read(1024).data().decode("utf-8") + self.assertEqual(response1, self.text) + + self.buffer.seek(0) + response2 = bytearray(1024) + bytes_read = self.buffer.read(response2, 1024) + + self.assertGreaterEqual(bytes_read, 0, self.buffer.errorString()) + self.assertEqual(response2[:bytes_read].decode("utf-8"), response1) + + def test_readLine(self) -> None: + response1 = self.buffer.readLine(1024).data().decode("utf-8") + # Only read until the first line (including the line break) + self.assertEqual(response1, self.text.split("\n", 1)[0] + "\n") + + self.buffer.seek(0) + response2 = bytearray(1024) + bytes_read = self.buffer.readLine(response2, 1024) + + self.assertGreaterEqual(bytes_read, 0, self.buffer.errorString()) + self.assertEqual(response2[:bytes_read].decode("utf-8"), response1) + + def test_peek(self) -> None: + response1 = self.buffer.peek(1024).data().decode("utf-8") + self.assertEqual(response1, self.text) + + # Test that peek has no side effects + response_again1 = self.buffer.read(1024).data().decode("utf-8") + self.assertEqual(response_again1, response1) + + self.buffer.seek(0) + response2 = bytearray(1024) + bytes_read = self.buffer.peek(response2, 1024) + + self.assertGreaterEqual(bytes_read, 0, self.buffer.errorString()) + self.assertEqual(response2[:bytes_read].decode("utf-8"), response1) + + # Test that peek has no side effects + response_again2 = bytearray(1024) + bytes_read_again2 = self.buffer.read(response_again2, 1024) + self.assertEqual(bytes_read, bytes_read_again2) + self.assertEqual(response_again2, response2) + + +if __name__ == "__main__": + unittest.main() |