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:
parent
fc1d3074de
commit
cfd6e6cc36
19 changed files with 112 additions and 112 deletions
42
LibCore/CFile.cpp
Normal file
42
LibCore/CFile.cpp
Normal 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
21
LibCore/CFile.h
Normal 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
175
LibCore/CIODevice.cpp
Normal 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
60
LibCore/CIODevice.h
Normal 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
93
LibCore/CSocket.cpp
Normal 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
44
LibCore/CSocket.h
Normal 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
31
LibCore/CSocketAddress.h
Normal 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
19
LibCore/CTCPSocket.cpp
Normal 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
10
LibCore/CTCPSocket.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include <LibCore/CSocket.h>
|
||||
|
||||
class CTCPSocket final : public CSocket {
|
||||
public:
|
||||
explicit CTCPSocket(CObject* parent = nullptr);
|
||||
virtual ~CTCPSocket() override;
|
||||
|
||||
private:
|
||||
};
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
OBJS = \
|
||||
CIODevice.o \
|
||||
CFile.o \
|
||||
CSocket.o \
|
||||
CTCPSocket.o \
|
||||
CElapsedTimer.o \
|
||||
CNotifier.o \
|
||||
CObject.o \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue