Skip to content

Commit

Permalink
Add a custom buffered filebuf that uses a file descriptor
Browse files Browse the repository at this point in the history
Implemented for the WiiU because IO through std::filebuf appears to be unbuffered.

But can maybe help on other platforms.

Can be toggled by setting USE_CUSTOM_FILE_READBUF to the prefered buffer size.
  • Loading branch information
Ghabry committed Jun 4, 2024
1 parent 192ab4c commit 77ac2bd
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 1 deletion.
20 changes: 19 additions & 1 deletion src/filesystem_native.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,21 @@
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <ios>
#include <string>
#include <vector>
#include <fmt/core.h>

#include "filesystem_stream.h"
#include "system.h"
#include "output.h"
#include "platform.h"

#ifdef USE_CUSTOM_FILE_READBUF
# include <sys/stat.h>
# include <fcntl.h>
#endif

NativeFilesystem::NativeFilesystem(std::string base_path, FilesystemView parent_fs) : Filesystem(std::move(base_path), parent_fs) {
}

Expand All @@ -48,7 +55,17 @@ int64_t NativeFilesystem::GetFilesize(StringView path) const {
}

std::streambuf* NativeFilesystem::CreateInputStreambuffer(StringView path, std::ios_base::openmode mode) const {
auto* buf = new std::filebuf();
#ifdef USE_CUSTOM_FILE_READBUF
(void)mode;
int fd = open(ToString(path).c_str(), O_RDONLY);
if (fd < 0) {
return nullptr;
}

return new Filesystem_Stream::FdStreamBuf(fd);
#else
auto buf = new std::filebuf();

buf->open(
#ifdef _MSC_VER
Utils::ToWideString(path),
Expand All @@ -63,6 +80,7 @@ std::streambuf* NativeFilesystem::CreateInputStreambuffer(StringView path, std::
}

return buf;
#endif
}

std::streambuf* NativeFilesystem::CreateOutputStreambuffer(StringView path, std::ios_base::openmode mode) const {
Expand Down
86 changes: 86 additions & 0 deletions src/filesystem_stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@

#include <utility>

#ifdef USE_CUSTOM_FILE_READBUF
# include <unistd.h>
#endif

Filesystem_Stream::InputStream::InputStream(std::streambuf* sb, std::string name) :
std::istream(sb), name(std::move(name)) {}

Expand Down Expand Up @@ -122,3 +126,85 @@ Filesystem_Stream::InputMemoryStreamBuf::InputMemoryStreamBuf(std::vector<uint8_
: InputMemoryStreamBufView(buffer), buffer(std::move(buffer)) {

}


#ifdef USE_CUSTOM_FILE_READBUF

Filesystem_Stream::FdStreamBuf::FdStreamBuf(int fd) : fd(fd) {
clear_buffer();
}

Filesystem_Stream::FdStreamBuf::~FdStreamBuf() {
close(fd);
}

Filesystem_Stream::FdStreamBuf::int_type Filesystem_Stream::FdStreamBuf::underflow() {
assert(gptr() == egptr());

auto bytes_read = read(fd, buffer.begin(), buffer.size());
if (bytes_read <= 0) {
return traits_type::eof();
}
file_offset += bytes_read;

setg(buffer.begin(), buffer.begin(), buffer.begin() + bytes_read);

return traits_type::to_int_type(*gptr());
}

std::streambuf::pos_type Filesystem_Stream::FdStreamBuf::seekoff(std::streambuf::off_type offset, std::ios_base::seekdir dir, std::ios_base::openmode) {
if (dir == std::ios_base::beg) {
offset = offset - file_offset + bytes_remaining();
dir = std::ios_base::cur;
}

if (dir == std::ios_base::cur) {
if (offset < 0) {
auto dist = std::distance(gptr(), gptr() - offset);
if (gptr() + offset < eback()) {
// Not cached: Outside of the buffer: Reposition the stream
file_offset = lseek(fd, -dist - bytes_remaining(), SEEK_CUR);
clear_buffer();
} else {
setg(buffer.begin(), gptr() - dist, egptr());
}
} else if (offset > 0) {
auto dist = std::distance(gptr(), gptr() + offset);
if (gptr() + offset > egptr()) {
// Not cached: Outside of the buffer: Reposition the stream
file_offset = lseek(fd, dist - bytes_remaining(), SEEK_CUR);
clear_buffer();
} else {
setg(buffer.begin(), gptr() + dist, egptr());
}
}
return file_offset - bytes_remaining();
} else {
// Not cached: Seek to end
clear_buffer();
file_offset = lseek(fd, offset, SEEK_END);

if (file_offset < 0) {
file_offset = 0;
return -1;
}

return file_offset;
}

assert(false);
}

std::streambuf::pos_type Filesystem_Stream::FdStreamBuf::seekpos(std::streambuf::pos_type pos, std::ios_base::openmode mode) {
return seekoff(pos, std::ios_base::beg, mode);
}

void Filesystem_Stream::FdStreamBuf::clear_buffer() {
setg(buffer.begin(), buffer.end(), buffer.end());
}

ssize_t Filesystem_Stream::FdStreamBuf::bytes_remaining() const {
return egptr() - gptr();
}

#endif
24 changes: 24 additions & 0 deletions src/filesystem_stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <ostream>
#include "filesystem.h"
#include "utils.h"
#include "system.h"

namespace Filesystem_Stream {
class InputStream final : public std::istream {
Expand Down Expand Up @@ -96,6 +97,29 @@ namespace Filesystem_Stream {
std::vector<uint8_t> buffer;
};

#ifdef USE_CUSTOM_FILE_READBUF
class FdStreamBuf : public std::streambuf {
public:
FdStreamBuf(int fd);
FdStreamBuf(FdStreamBuf const& other) = delete;
FdStreamBuf const& operator=(FdStreamBuf const& other) = delete;
~FdStreamBuf();

protected:
int_type underflow();
std::streambuf::pos_type seekoff(std::streambuf::off_type offset, std::ios_base::seekdir dir, std::ios_base::openmode mode);
std::streambuf::pos_type seekpos(std::streambuf::pos_type pos, std::ios_base::openmode);

private:
void clear_buffer();
ssize_t bytes_remaining() const;

int fd;
off_t file_offset = 0;
std::array<char, USE_CUSTOM_FILE_READBUF> buffer;
};
#endif

static constexpr std::ios_base::seekdir CSeekdirToCppSeekdir(int origin);

static constexpr int CppSeekdirToCSeekdir(std::ios_base::seekdir origin);
Expand Down
1 change: 1 addition & 0 deletions src/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
# define SUPPORT_JOYSTICK
# define SUPPORT_JOYSTICK_AXIS
# define SUPPORT_TOUCH
# define USE_CUSTOM_FILE_READBUF 16 * 1024
#elif defined(_WIN32)
# define SUPPORT_ZOOM
# define SUPPORT_MOUSE
Expand Down

0 comments on commit 77ac2bd

Please sign in to comment.