#include #include #include #include GIODevice::GIODevice(GObject* parent) : GObject(parent) { } GIODevice::~GIODevice() { } const char* GIODevice::error_string() const { return strerror(m_error); } ByteBuffer GIODevice::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 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 { }; } buffer.trim(nread); return buffer; } bool GIODevice::can_read_from_fd() const { // FIXME: Can we somehow remove this once GSocket 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("GIODevice::can_read: select"); return false; } return FD_ISSET(m_fd, &rfds); } bool GIODevice::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 GIODevice::can_read() const { return !m_buffered_data.is_empty() || can_read_from_fd(); } ByteBuffer GIODevice::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 GIODevice::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) { dbgprintf("GIODevice::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 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 GIODevice::populate_read_buffer() { if (m_fd < 0) return false; auto buffer = ByteBuffer::create_uninitialized(1024); 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 GIODevice::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(GIODevice::NotOpen); return true; }