Move the ifstream wrapper to common

This commit is contained in:
Chris Robinson
2019-09-22 12:23:41 -07:00
parent 9c95f62e95
commit 95996effaf
8 changed files with 228 additions and 209 deletions
+2
View File
@@ -527,6 +527,8 @@ SET(COMMON_OBJS
common/alcomplex.h
common/alexcpt.cpp
common/alexcpt.h
common/alfstream.cpp
common/alfstream.h
common/almalloc.cpp
common/almalloc.h
common/alnumeric.h
+6 -4
View File
@@ -41,15 +41,17 @@
#include <CoreFoundation/CoreFoundation.h>
#endif
#include <vector>
#include <string>
#include <algorithm>
#include <cstdio>
#include <string>
#include <utility>
#include "alcmain.h"
#include "alfstream.h"
#include "alstring.h"
#include "compat.h"
#include "logging.h"
#include "strutils.h"
#include "compat.h"
#include "vector.h"
namespace {
+1 -1
View File
@@ -10,7 +10,7 @@
#include <sstream>
#include <string>
#include "compat.h"
#include "alfstream.h"
#include "logging.h"
-66
View File
@@ -1,72 +1,6 @@
#ifndef AL_COMPAT_H
#define AL_COMPAT_H
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <array>
#include <string>
#include <fstream>
namespace al {
// Windows' std::ifstream fails with non-ANSI paths since the standard only
// specifies names using const char* (or std::string). MSVC has a non-standard
// extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
// but not all Windows compilers support it. So we have to make our own istream
// that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
class filebuf final : public std::streambuf {
std::array<char_type,4096> mBuffer;
HANDLE mFile{INVALID_HANDLE_VALUE};
int_type underflow() override;
pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override;
pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override;
public:
filebuf() = default;
~filebuf() override;
bool open(const wchar_t *filename, std::ios_base::openmode mode);
bool open(const char *filename, std::ios_base::openmode mode);
bool is_open() const noexcept { return mFile != INVALID_HANDLE_VALUE; }
};
// Inherit from std::istream to use our custom streambuf
class ifstream final : public std::istream {
filebuf mStreamBuf;
public:
ifstream(const wchar_t *filename, std::ios_base::openmode mode = std::ios_base::in);
ifstream(const std::wstring &filename, std::ios_base::openmode mode = std::ios_base::in)
: ifstream(filename.c_str(), mode) { }
ifstream(const char *filename, std::ios_base::openmode mode = std::ios_base::in);
ifstream(const std::string &filename, std::ios_base::openmode mode = std::ios_base::in)
: ifstream(filename.c_str(), mode) { }
~ifstream() override;
bool is_open() const noexcept { return mStreamBuf.is_open(); }
};
} // namespace al
#else /* _WIN32 */
#include <fstream>
namespace al {
using filebuf = std::filebuf;
using ifstream = std::ifstream;
} // namespace al
#endif /* _WIN32 */
#include <string>
struct PathNamePair { std::string path, fname; };
-137
View File
@@ -248,143 +248,6 @@ void FPUCtl::leave()
#ifdef _WIN32
namespace al {
auto filebuf::underflow() -> int_type
{
if(mFile != INVALID_HANDLE_VALUE && gptr() == egptr())
{
// Read in the next chunk of data, and set the pointers on success
DWORD got{};
if(ReadFile(mFile, mBuffer.data(), static_cast<DWORD>(mBuffer.size()), &got, nullptr))
setg(mBuffer.data(), mBuffer.data(), mBuffer.data()+got);
}
if(gptr() == egptr())
return traits_type::eof();
return traits_type::to_int_type(*gptr());
}
auto filebuf::seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) -> pos_type
{
if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
return traits_type::eof();
LARGE_INTEGER fpos{};
switch(whence)
{
case std::ios_base::beg:
fpos.QuadPart = offset;
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
return traits_type::eof();
break;
case std::ios_base::cur:
// If the offset remains in the current buffer range, just
// update the pointer.
if((offset >= 0 && offset < off_type(egptr()-gptr())) ||
(offset < 0 && -offset <= off_type(gptr()-eback())))
{
// Get the current file offset to report the correct read
// offset.
fpos.QuadPart = 0;
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
return traits_type::eof();
setg(eback(), gptr()+offset, egptr());
return fpos.QuadPart - off_type(egptr()-gptr());
}
// Need to offset for the file offset being at egptr() while
// the requested offset is relative to gptr().
offset -= off_type(egptr()-gptr());
fpos.QuadPart = offset;
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
return traits_type::eof();
break;
case std::ios_base::end:
fpos.QuadPart = offset;
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_END))
return traits_type::eof();
break;
default:
return traits_type::eof();
}
setg(nullptr, nullptr, nullptr);
return fpos.QuadPart;
}
auto filebuf::seekpos(pos_type pos, std::ios_base::openmode mode) -> pos_type
{
// Simplified version of seekoff
if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
return traits_type::eof();
LARGE_INTEGER fpos{};
fpos.QuadPart = pos;
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
return traits_type::eof();
setg(nullptr, nullptr, nullptr);
return fpos.QuadPart;
}
filebuf::~filebuf()
{
if(mFile != INVALID_HANDLE_VALUE)
CloseHandle(mFile);
mFile = INVALID_HANDLE_VALUE;
}
bool filebuf::open(const wchar_t *filename, std::ios_base::openmode mode)
{
if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
return false;
HANDLE f{CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, nullptr)};
if(f == INVALID_HANDLE_VALUE) return false;
if(mFile != INVALID_HANDLE_VALUE)
CloseHandle(mFile);
mFile = f;
setg(nullptr, nullptr, nullptr);
return true;
}
bool filebuf::open(const char *filename, std::ios_base::openmode mode)
{
std::wstring wname{utf8_to_wstr(filename)};
return open(wname.c_str(), mode);
}
ifstream::ifstream(const wchar_t *filename, std::ios_base::openmode mode)
: std::istream{nullptr}
{
init(&mStreamBuf);
// Set the failbit if the file failed to open.
if((mode&std::ios_base::out) || !mStreamBuf.open(filename, mode|std::ios_base::in))
clear(failbit);
}
ifstream::ifstream(const char *filename, std::ios_base::openmode mode)
: std::istream{nullptr}
{
init(&mStreamBuf);
// Set the failbit if the file failed to open.
if((mode&std::ios_base::out) || !mStreamBuf.open(filename, mode|std::ios_base::in))
clear(failbit);
}
/* This is only here to ensure the compiler doesn't define an implicit
* destructor, which it tries to automatically inline and subsequently complain
* it can't inline without excessive code growth.
*/
ifstream::~ifstream() { }
} // namespace al
const PathNamePair &GetProcBinary()
{
static PathNamePair ret;
+2 -1
View File
@@ -36,17 +36,18 @@
#include <mutex>
#include <new>
#include <numeric>
#include <type_traits>
#include <utility>
#include "AL/al.h"
#include "alcmain.h"
#include "alconfig.h"
#include "alfstream.h"
#include "almalloc.h"
#include "alnumeric.h"
#include "aloptional.h"
#include "alspan.h"
#include "compat.h"
#include "filters/splitter.h"
#include "logging.h"
#include "math_defs.h"
+147
View File
@@ -0,0 +1,147 @@
#include "config.h"
#include "alfstream.h"
#include "strutils.h"
#ifdef _WIN32
namespace al {
auto filebuf::underflow() -> int_type
{
if(mFile != INVALID_HANDLE_VALUE && gptr() == egptr())
{
// Read in the next chunk of data, and set the pointers on success
DWORD got{};
if(ReadFile(mFile, mBuffer.data(), static_cast<DWORD>(mBuffer.size()), &got, nullptr))
setg(mBuffer.data(), mBuffer.data(), mBuffer.data()+got);
}
if(gptr() == egptr())
return traits_type::eof();
return traits_type::to_int_type(*gptr());
}
auto filebuf::seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) -> pos_type
{
if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
return traits_type::eof();
LARGE_INTEGER fpos{};
switch(whence)
{
case std::ios_base::beg:
fpos.QuadPart = offset;
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
return traits_type::eof();
break;
case std::ios_base::cur:
// If the offset remains in the current buffer range, just
// update the pointer.
if((offset >= 0 && offset < off_type(egptr()-gptr())) ||
(offset < 0 && -offset <= off_type(gptr()-eback())))
{
// Get the current file offset to report the correct read
// offset.
fpos.QuadPart = 0;
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
return traits_type::eof();
setg(eback(), gptr()+offset, egptr());
return fpos.QuadPart - off_type(egptr()-gptr());
}
// Need to offset for the file offset being at egptr() while
// the requested offset is relative to gptr().
offset -= off_type(egptr()-gptr());
fpos.QuadPart = offset;
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
return traits_type::eof();
break;
case std::ios_base::end:
fpos.QuadPart = offset;
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_END))
return traits_type::eof();
break;
default:
return traits_type::eof();
}
setg(nullptr, nullptr, nullptr);
return fpos.QuadPart;
}
auto filebuf::seekpos(pos_type pos, std::ios_base::openmode mode) -> pos_type
{
// Simplified version of seekoff
if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
return traits_type::eof();
LARGE_INTEGER fpos{};
fpos.QuadPart = pos;
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
return traits_type::eof();
setg(nullptr, nullptr, nullptr);
return fpos.QuadPart;
}
filebuf::~filebuf()
{
if(mFile != INVALID_HANDLE_VALUE)
CloseHandle(mFile);
mFile = INVALID_HANDLE_VALUE;
}
bool filebuf::open(const wchar_t *filename, std::ios_base::openmode mode)
{
if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
return false;
HANDLE f{CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, nullptr)};
if(f == INVALID_HANDLE_VALUE) return false;
if(mFile != INVALID_HANDLE_VALUE)
CloseHandle(mFile);
mFile = f;
setg(nullptr, nullptr, nullptr);
return true;
}
bool filebuf::open(const char *filename, std::ios_base::openmode mode)
{
std::wstring wname{utf8_to_wstr(filename)};
return open(wname.c_str(), mode);
}
ifstream::ifstream(const wchar_t *filename, std::ios_base::openmode mode)
: std::istream{nullptr}
{
init(&mStreamBuf);
// Set the failbit if the file failed to open.
if((mode&std::ios_base::out) || !mStreamBuf.open(filename, mode|std::ios_base::in))
clear(failbit);
}
ifstream::ifstream(const char *filename, std::ios_base::openmode mode)
: std::istream{nullptr}
{
init(&mStreamBuf);
// Set the failbit if the file failed to open.
if((mode&std::ios_base::out) || !mStreamBuf.open(filename, mode|std::ios_base::in))
clear(failbit);
}
/* This is only here to ensure the compiler doesn't define an implicit
* destructor, which it tries to automatically inline and subsequently complain
* it can't inline without excessive code growth.
*/
ifstream::~ifstream() { }
} // namespace al
#endif
+70
View File
@@ -0,0 +1,70 @@
#ifndef AL_FSTREAM_H
#define AL_FSTREAM_H
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <array>
#include <string>
#include <fstream>
namespace al {
// Windows' std::ifstream fails with non-ANSI paths since the standard only
// specifies names using const char* (or std::string). MSVC has a non-standard
// extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
// but not all Windows compilers support it. So we have to make our own istream
// that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
class filebuf final : public std::streambuf {
std::array<char_type,4096> mBuffer;
HANDLE mFile{INVALID_HANDLE_VALUE};
int_type underflow() override;
pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override;
pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override;
public:
filebuf() = default;
~filebuf() override;
bool open(const wchar_t *filename, std::ios_base::openmode mode);
bool open(const char *filename, std::ios_base::openmode mode);
bool is_open() const noexcept { return mFile != INVALID_HANDLE_VALUE; }
};
// Inherit from std::istream to use our custom streambuf
class ifstream final : public std::istream {
filebuf mStreamBuf;
public:
ifstream(const wchar_t *filename, std::ios_base::openmode mode = std::ios_base::in);
ifstream(const std::wstring &filename, std::ios_base::openmode mode = std::ios_base::in)
: ifstream(filename.c_str(), mode) { }
ifstream(const char *filename, std::ios_base::openmode mode = std::ios_base::in);
ifstream(const std::string &filename, std::ios_base::openmode mode = std::ios_base::in)
: ifstream(filename.c_str(), mode) { }
~ifstream() override;
bool is_open() const noexcept { return mStreamBuf.is_open(); }
};
} // namespace al
#else /* _WIN32 */
#include <fstream>
namespace al {
using filebuf = std::filebuf;
using ifstream = std::ifstream;
} // namespace al
#endif /* _WIN32 */
#endif /* AL_FSTREAM_H */