summaryrefslogtreecommitdiff
path: root/LibCore/CIODevice.cpp
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2019-04-10 20:22:23 +0200
committerAndreas Kling <awesomekling@gmail.com>2019-04-10 20:22:23 +0200
commitcfd6e6cc3686ff5d9a10793cddf9d024a6bfac6d (patch)
tree5e28969da6f3f2ac1cc51fd9739edbfafe31d4ee /LibCore/CIODevice.cpp
parentfc1d3074de27fbcd0bf07e61781850f628fba0f1 (diff)
downloadserenity-cfd6e6cc3686ff5d9a10793cddf9d024a6bfac6d.zip
LibCore: Move GIODevice hierarchy from LibGUI to LibCore.
Diffstat (limited to 'LibCore/CIODevice.cpp')
-rw-r--r--LibCore/CIODevice.cpp175
1 files changed, 175 insertions, 0 deletions
diff --git a/LibCore/CIODevice.cpp b/LibCore/CIODevice.cpp
new file mode 100644
index 0000000000..80ac9ef18e
--- /dev/null
+++ b/LibCore/CIODevice.cpp
@@ -0,0 +1,175 @@
+#include <LibCore/CIODevice.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <stdio.h>
+
+CIODevice::CIODevice(CObject* parent)
+ : CObject(parent)
+{
+}
+
+CIODevice::~CIODevice()
+{
+}
+
+const char* CIODevice::error_string() const
+{
+ return strerror(m_error);
+}
+
+ByteBuffer CIODevice::read(int max_size)
+{
+ if (m_fd < 0)
+ return { };
+ if (!max_size)
+ return { };
+ auto buffer = ByteBuffer::create_uninitialized(max_size);
+ auto* buffer_ptr = (char*)buffer.pointer();
+ int remaining_buffer_space = buffer.size();
+ if (!m_buffered_data.is_empty()) {
+ int taken_from_buffered = min(remaining_buffer_space, m_buffered_data.size());
+ memcpy(buffer_ptr, m_buffered_data.data(), taken_from_buffered);
+ Vector<byte> new_buffered_data;
+ new_buffered_data.append(m_buffered_data.data() + taken_from_buffered, m_buffered_data.size() - taken_from_buffered);
+ m_buffered_data = move(new_buffered_data);
+ remaining_buffer_space -= taken_from_buffered;
+ buffer_ptr += taken_from_buffered;
+ }
+ if (!remaining_buffer_space)
+ return buffer;
+ int nread = ::read(m_fd, buffer_ptr, remaining_buffer_space);
+ if (nread < 0) {
+ set_error(errno);
+ return { };
+ }
+ if (nread == 0) {
+ set_eof(true);
+ return { };
+ }
+ buffer.trim(nread);
+ return buffer;
+}
+
+bool CIODevice::can_read_from_fd() const
+{
+ // FIXME: Can we somehow remove this once CSocket is implemented using non-blocking sockets?
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(m_fd, &rfds);
+ struct timeval timeout { 0, 0 };
+ int rc = select(m_fd + 1, &rfds, nullptr, nullptr, &timeout);
+ if (rc < 0) {
+ // NOTE: We don't set m_error here.
+ perror("CIODevice::can_read: select");
+ return false;
+ }
+ return FD_ISSET(m_fd, &rfds);
+}
+
+bool CIODevice::can_read_line()
+{
+ if (m_eof && !m_buffered_data.is_empty())
+ return true;
+ if (m_buffered_data.contains_slow('\n'))
+ return true;
+ if (!can_read_from_fd())
+ return false;
+ populate_read_buffer();
+ return m_buffered_data.contains_slow('\n');
+}
+
+bool CIODevice::can_read() const
+{
+ return !m_buffered_data.is_empty() || can_read_from_fd();
+}
+
+ByteBuffer CIODevice::read_all()
+{
+ ByteBuffer buffer;
+ if (!m_buffered_data.is_empty()) {
+ buffer = ByteBuffer::copy(m_buffered_data.data(), m_buffered_data.size());
+ m_buffered_data.clear();
+ }
+
+ while (can_read_from_fd()) {
+ char read_buffer[4096];
+ int nread = ::read(m_fd, read_buffer, sizeof(read_buffer));
+ if (nread < 0) {
+ set_error(nread);
+ return buffer;
+ }
+ if (nread == 0) {
+ set_eof(true);
+ break;
+ }
+ buffer.append(read_buffer, nread);
+ }
+ return buffer;
+}
+
+ByteBuffer CIODevice::read_line(int max_size)
+{
+ if (m_fd < 0)
+ return { };
+ if (!max_size)
+ return { };
+ if (!can_read_line())
+ return { };
+ if (m_eof) {
+ if (m_buffered_data.size() > max_size) {
+ printf("CIODevice::read_line: At EOF but there's more than max_size(%d) buffered\n", max_size);
+ return { };
+ }
+ auto buffer = ByteBuffer::copy(m_buffered_data.data(), m_buffered_data.size());
+ m_buffered_data.clear();
+ return buffer;
+ }
+ auto line = ByteBuffer::create_uninitialized(max_size + 1);
+ int line_index = 0;
+ while (line_index < max_size) {
+ byte ch = m_buffered_data[line_index];
+ line[line_index++] = ch;
+ if (ch == '\n') {
+ Vector<byte> new_buffered_data;
+ new_buffered_data.append(m_buffered_data.data() + line_index, m_buffered_data.size() - line_index);
+ m_buffered_data = move(new_buffered_data);
+ line[line_index] = '\0';
+ line.trim(line_index + 1);
+ return line;
+ }
+ }
+ return { };
+}
+
+bool CIODevice::populate_read_buffer()
+{
+ if (m_fd < 0)
+ return false;
+ auto buffer = ByteBuffer::create_uninitialized(PAGE_SIZE);
+ int nread = ::read(m_fd, buffer.pointer(), buffer.size());
+ if (nread < 0) {
+ set_error(errno);
+ return false;
+ }
+ if (nread == 0) {
+ set_eof(true);
+ return false;
+ }
+ buffer.trim(nread);
+ m_buffered_data.append(buffer.pointer(), buffer.size());
+ return true;
+}
+
+bool CIODevice::close()
+{
+ if (fd() < 0 || mode() == NotOpen)
+ return false;
+ int rc = ::close(fd());
+ if (rc < 0) {
+ set_error(rc);
+ return false;
+ }
+ set_fd(-1);
+ set_mode(CIODevice::NotOpen);
+ return true;
+}