158 lines
4.5 KiB
C++
Executable File
158 lines
4.5 KiB
C++
Executable File
#include "file_source.h"
|
|
|
|
#include "cpp11_compat.h"
|
|
#include "file_direct_reader.h"
|
|
#include "logging.h"
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
// Get the file size or zero if unable to open the file.
|
|
inline size_t getFileSize(const std::string& filepath) {
|
|
struct stat st;
|
|
if (stat(filepath.c_str(), &st) != 0) {
|
|
return 0;
|
|
}
|
|
return st.st_size;
|
|
}
|
|
|
|
FileSource::FileSource(const std::string& path) : mPath(path), kValidFileSize(getFileSize(path)) {}
|
|
|
|
FileSource::FileSource(const void* data, const size_t size, const std::string& name)
|
|
: mName(name), mFileData((const char*)data, size) {}
|
|
|
|
// Check if the file source is used, aka not empty
|
|
FileSource::operator bool() const {
|
|
return !empty();
|
|
}
|
|
|
|
// Check if the file source is empty
|
|
bool FileSource::empty() const {
|
|
return (mPath.empty() && mFileData.empty());
|
|
}
|
|
|
|
// Return the path if possible, otherwise return the given name.
|
|
const std::string& FileSource::getName() const {
|
|
static const std::string unnamed = "Unnamed";
|
|
if (!mPath.empty())
|
|
return mPath;
|
|
if (mName.empty())
|
|
return unnamed;
|
|
return mName;
|
|
}
|
|
|
|
const char* FileSource::getData() const {
|
|
if (!valid()) {
|
|
LOG(WARN) << "Unable to load " << *this;
|
|
}
|
|
return getFileData().data();
|
|
}
|
|
|
|
size_t FileSource::getSize() const {
|
|
if (kValidFileSize > 0) {
|
|
return kValidFileSize;
|
|
}
|
|
if (!valid()) {
|
|
LOG(WARN) << "Unable to load " << *this;
|
|
}
|
|
return getFileData().size();
|
|
}
|
|
|
|
std::pair<const char*, size_t> FileSource::get() const {
|
|
if (!valid()) {
|
|
LOG(WARN) << "Unable to load " << *this;
|
|
}
|
|
const auto& fileData = getFileData();
|
|
return {fileData.data(), fileData.size()};
|
|
}
|
|
|
|
// Returns whether the file can be read successfully
|
|
bool FileSource::valid() const {
|
|
return (kValidFileSize > 0) || !getFileData().empty();
|
|
}
|
|
|
|
// Check if the FileSource instance owns and manages the buffer lifecycle
|
|
bool FileSource::hasBufferOwnership() const {
|
|
return !mPath.empty();
|
|
}
|
|
|
|
// Load the file if not yet loaded and has path given.
|
|
// Returns true if file is loaded successfully, false if otherwise.
|
|
bool FileSource::load() {
|
|
if (!mFileData.empty() || mPath.empty()) {
|
|
return true;
|
|
}
|
|
DCHECK(!mFileMemMapper);
|
|
mFileMemMapper = std::make_shared<FileMemMapper>(mPath);
|
|
if (mFileMemMapper->valid()) {
|
|
const auto fileData = mFileMemMapper->get();
|
|
const auto data = fileData.first;
|
|
const auto size = fileData.second;
|
|
mFileData = mtk::cpp11_compat::string_view(data, size);
|
|
}
|
|
return mFileMemMapper->valid();
|
|
}
|
|
|
|
bool FileSource::hint_release() const {
|
|
if (!hasBufferOwnership()) {
|
|
return false; // Unable to reopen without having the path
|
|
}
|
|
releaseFileData();
|
|
return true;
|
|
}
|
|
|
|
const mtk::cpp11_compat::string_view& FileSource::getFileData() const {
|
|
const_cast<FileSource*>(this)->load();
|
|
return mFileData;
|
|
}
|
|
|
|
void FileSource::releaseFileData() const {
|
|
if (mFileMemMapper) {
|
|
mFileMemMapper.reset();
|
|
}
|
|
mFileData = mtk::cpp11_compat::string_view();
|
|
}
|
|
|
|
bool FileSource::directRead(void* dstBuffer, const size_t dstSize, const bool allowFallback) const {
|
|
if (!valid()) {
|
|
LOG(WARN) << "Unable to load " << *this;
|
|
return false;
|
|
}
|
|
|
|
auto checkSize = [=](const size_t fileSize) {
|
|
if (dstSize < fileSize) {
|
|
LOG(WARN) << "Destination buffer size (" << dstSize
|
|
<< ") is smaller than the file size (" << fileSize << ").";
|
|
}
|
|
};
|
|
|
|
if (!mPath.empty()) {
|
|
FileDirectReader directIOLoader(mPath);
|
|
checkSize(directIOLoader.size());
|
|
const auto status = directIOLoader.load(dstBuffer, dstSize);
|
|
if (status) {
|
|
return true; // Successfully loaded using Direct IO
|
|
} else if (!allowFallback) {
|
|
LOG(FATAL) << "Unable to load file using Direct IO: " << *this;
|
|
} else {
|
|
LOG(WARN) << "Failed to load file using Direct IO, falling back to mmap.";
|
|
}
|
|
}
|
|
|
|
// Fallback to loading manually using memory copy from file data that may be mmapped
|
|
const auto fileInfo = get();
|
|
const auto fileData = fileInfo.first;
|
|
const auto fileSize = fileInfo.second;
|
|
checkSize(fileSize);
|
|
const auto copySize = std::min(dstSize, fileSize);
|
|
std::memcpy(dstBuffer, fileData, copySize);
|
|
return true;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& stream, const FileSource& fileSource) {
|
|
stream << "<FileSource: " << (fileSource.empty() ? "None" : fileSource.getName()) << ">";
|
|
return stream;
|
|
} |