1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 00:57:45 +00:00

LibCore: Move GIODevice hierarchy from LibGUI to LibCore.

This commit is contained in:
Andreas Kling 2019-04-10 20:22:23 +02:00
parent fc1d3074de
commit cfd6e6cc36
19 changed files with 112 additions and 112 deletions

42
LibCore/CFile.cpp Normal file
View file

@ -0,0 +1,42 @@
#include <LibCore/CFile.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
CFile::CFile(const String& filename)
: m_filename(filename)
{
}
CFile::~CFile()
{
if (mode() != NotOpen)
close();
}
bool CFile::open(CIODevice::OpenMode mode)
{
int flags = 0;
if ((mode & CIODevice::ReadWrite) == CIODevice::ReadWrite) {
flags |= O_RDWR | O_CREAT;
} else if (mode & CIODevice::ReadOnly) {
flags |= O_RDONLY;
} else if (mode & CIODevice::WriteOnly) {
flags |= O_WRONLY | O_CREAT;
}
if (mode & CIODevice::Append)
flags |= O_APPEND;
if (mode & CIODevice::Truncate)
flags |= O_TRUNC;
if (mode & CIODevice::MustBeNew)
flags |= O_EXCL;
int fd = ::open(m_filename.characters(), flags, 0666);
if (fd < 0) {
set_error(errno);
return false;
}
set_fd(fd);
set_mode(mode);
return true;
}

21
LibCore/CFile.h Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include <LibCore/CIODevice.h>
#include <AK/AKString.h>
class CFile final : public CIODevice {
public:
CFile() { }
explicit CFile(const String&);
virtual ~CFile() override;
String filename() const { return m_filename; }
void set_filename(const String& filename) { m_filename = filename; }
virtual bool open(CIODevice::OpenMode) override;
virtual const char* class_name() const override { return "CFile"; }
private:
String m_filename;
};

175
LibCore/CIODevice.cpp Normal file
View file

@ -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;
}

60
LibCore/CIODevice.h Normal file
View file

@ -0,0 +1,60 @@
#pragma once
#include <LibCore/CObject.h>
#include <AK/ByteBuffer.h>
class CIODevice : public CObject {
public:
enum OpenMode {
NotOpen = 0,
ReadOnly = 1,
WriteOnly = 2,
ReadWrite = 3,
Append = 4,
Truncate = 8,
MustBeNew = 16,
};
virtual ~CIODevice() override;
int fd() const { return m_fd; }
unsigned mode() const { return m_mode; }
bool eof() const { return m_eof; }
int error() const { return m_error; }
const char* error_string() const;
bool has_error() const { return m_error != 0; }
ByteBuffer read(int max_size);
ByteBuffer read_line(int max_size);
ByteBuffer read_all();
// FIXME: I would like this to be const but currently it needs to call populate_read_buffer().
bool can_read_line();
bool can_read() const;
virtual bool open(CIODevice::OpenMode) = 0;
virtual bool close();
virtual const char* class_name() const override { return "CIODevice"; }
protected:
explicit CIODevice(CObject* parent = nullptr);
void set_fd(int fd) { m_fd = fd; }
void set_mode(OpenMode mode) { m_mode = mode; }
void set_error(int error) { m_error = error; }
void set_eof(bool eof) { m_eof = eof; }
private:
bool populate_read_buffer();
bool can_read_from_fd() const;
int m_fd { -1 };
int m_error { 0 };
bool m_eof { false };
OpenMode m_mode { NotOpen };
Vector<byte> m_buffered_data;
};

93
LibCore/CSocket.cpp Normal file
View file

@ -0,0 +1,93 @@
#include <LibCore/CSocket.h>
#include <LibCore/CNotifier.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
#include <errno.h>
CSocket::CSocket(Type type, CObject* parent)
: CIODevice(parent)
, m_type(type)
{
}
CSocket::~CSocket()
{
}
bool CSocket::connect(const String& hostname, int port)
{
auto* hostent = gethostbyname(hostname.characters());
if (!hostent) {
dbgprintf("CSocket::connect: Unable to resolve '%s'\n", hostname.characters());
return false;
}
IPv4Address host_address((const byte*)hostent->h_addr_list[0]);
dbgprintf("CSocket::connect: Resolved '%s' to %s\n", hostname.characters(), host_address.to_string().characters());
return connect(host_address, port);
}
bool CSocket::connect(const CSocketAddress& address, int port)
{
ASSERT(!is_connected());
ASSERT(address.type() == CSocketAddress::Type::IPv4);
ASSERT(port > 0 && port <= 65535);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
auto ipv4_address = address.ipv4_address();
memcpy(&addr.sin_addr.s_addr, &ipv4_address, sizeof(IPv4Address));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
m_destination_address = address;
m_destination_port = port;
printf("Connecting to %s...", address.to_string().characters());
fflush(stdout);
int rc = ::connect(fd(), (struct sockaddr*)&addr, sizeof(addr));
if (rc < 0) {
if (errno == EINPROGRESS) {
printf("in progress.\n");
m_notifier = make<CNotifier>(fd(), CNotifier::Event::Write);
m_notifier->on_ready_to_write = [this] {
printf("%s{%p} connected!\n", class_name(), this);
m_connected = true;
m_notifier->set_event_mask(CNotifier::Event::None);
if (on_connected)
on_connected();
};
return true;
}
perror("connect");
exit(1);
}
printf("ok!\n");
m_connected = true;
return true;
}
ByteBuffer CSocket::receive(int max_size)
{
auto buffer = read(max_size);
if (eof()) {
dbgprintf("CSocket{%p}: Connection appears to have closed in receive().\n", this);
m_connected = false;
}
return buffer;
}
bool CSocket::send(const ByteBuffer& data)
{
int nsent = ::send(fd(), data.pointer(), data.size(), 0);
if (nsent < 0) {
set_error(nsent);
return false;
}
ASSERT(nsent == data.size());
return true;
}

44
LibCore/CSocket.h Normal file
View file

@ -0,0 +1,44 @@
#pragma once
#include <LibCore/CIODevice.h>
#include <LibCore/CSocketAddress.h>
class CNotifier;
class CSocket : public CIODevice {
public:
enum class Type { Invalid, TCP, UDP };
virtual ~CSocket() override;
bool connect(const String& hostname, int port);
bool connect(const CSocketAddress&, int port);
ByteBuffer receive(int max_size);
bool send(const ByteBuffer&);
bool is_connected() const { return m_connected; }
CSocketAddress source_address() const { return m_source_address; }
int source_port() const { return m_source_port; }
CSocketAddress destination_address() const { return m_source_address; }
int destination_port() const { return m_destination_port; }
Function<void()> on_connected;
virtual const char* class_name() const override { return "CSocket"; }
protected:
CSocket(Type, CObject* parent);
CSocketAddress m_source_address;
CSocketAddress m_destination_address;
int m_source_port { -1 };
int m_destination_port { -1 };
bool m_connected { false };
private:
virtual bool open(CIODevice::OpenMode) override { ASSERT_NOT_REACHED(); }
Type m_type { Type::Invalid };
OwnPtr<CNotifier> m_notifier;
};

31
LibCore/CSocketAddress.h Normal file
View file

@ -0,0 +1,31 @@
#pragma once
#include <Kernel/Net/IPv4.h>
class CSocketAddress {
public:
enum class Type { Invalid, IPv4, Local };
CSocketAddress() { }
CSocketAddress(const IPv4Address& address)
: m_type(Type::IPv4)
, m_ipv4_address(address)
{
}
Type type() const { return m_type; }
bool is_valid() const { return m_type != Type::Invalid; }
IPv4Address ipv4_address() const { return m_ipv4_address; }
String to_string() const
{
switch (m_type) {
case Type::IPv4: return m_ipv4_address.to_string();
default: return "[CSocketAddress]";
}
}
private:
Type m_type { Type::Invalid };
IPv4Address m_ipv4_address;
};

19
LibCore/CTCPSocket.cpp Normal file
View file

@ -0,0 +1,19 @@
#include <LibCore/CTCPSocket.h>
#include <sys/socket.h>
CTCPSocket::CTCPSocket(CObject* parent)
: CSocket(CSocket::Type::TCP, parent)
{
int fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (fd < 0) {
set_error(fd);
} else {
set_fd(fd);
set_mode(CIODevice::ReadWrite);
set_error(0);
}
}
CTCPSocket::~CTCPSocket()
{
}

10
LibCore/CTCPSocket.h Normal file
View file

@ -0,0 +1,10 @@
#include <LibCore/CSocket.h>
class CTCPSocket final : public CSocket {
public:
explicit CTCPSocket(CObject* parent = nullptr);
virtual ~CTCPSocket() override;
private:
};

View file

@ -1,4 +1,8 @@
OBJS = \
CIODevice.o \
CFile.o \
CSocket.o \
CTCPSocket.o \
CElapsedTimer.o \
CNotifier.o \
CObject.o \