Extract the rest of the mmap logic from the PCM provider. Closes #934.
This commit is contained in:
parent
3222275750
commit
6c14c9bee9
4 changed files with 74 additions and 53 deletions
|
@ -22,12 +22,17 @@
|
||||||
#include "libaegisub/util.h"
|
#include "libaegisub/util.h"
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/interprocess/mapped_region.hpp>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace boost::interprocess;
|
using namespace boost::interprocess;
|
||||||
|
|
||||||
namespace agi {
|
namespace agi {
|
||||||
file_mapping::file_mapping(agi::fs::path const& filename, mode_t mode)
|
file_mapping::file_mapping(agi::fs::path const& filename, boost::interprocess::mode_t mode)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
: handle(CreateFileW(filename.wstring().c_str(), (unsigned int)mode, 0, nullptr, OPEN_EXISTING, 0, 0))
|
: handle(CreateFileW(filename.wstring().c_str(), (unsigned int)mode, 0, nullptr, OPEN_EXISTING, 0, 0))
|
||||||
{
|
{
|
||||||
|
@ -63,4 +68,48 @@ file_mapping::~file_mapping() {
|
||||||
ipcdetail::close_file(handle);
|
ipcdetail::close_file(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
read_file_mapping::read_file_mapping(fs::path const& filename)
|
||||||
|
: file(filename, read_only)
|
||||||
|
{
|
||||||
|
offset_t size;
|
||||||
|
ipcdetail::get_file_size(file.get_mapping_handle().handle, size);
|
||||||
|
file_size = static_cast<uint64_t>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
read_file_mapping::~read_file_mapping() { }
|
||||||
|
|
||||||
|
char *read_file_mapping::read(int64_t s_offset, uint64_t length) {
|
||||||
|
auto offset = static_cast<uint64_t>(s_offset);
|
||||||
|
if (offset + length > file_size)
|
||||||
|
throw InternalError("Attempted to map beyond end of file", nullptr);
|
||||||
|
|
||||||
|
// Check if we can just use the current mapping
|
||||||
|
if (region && offset >= mapping_start && offset + length <= mapping_start + region->get_size())
|
||||||
|
return static_cast<char *>(region->get_address()) + offset - mapping_start;
|
||||||
|
|
||||||
|
if (sizeof(size_t) == 4) {
|
||||||
|
mapping_start = offset & ~0xFFFFFULL; // Align to 1 MB bondary
|
||||||
|
length += static_cast<size_t>(offset - mapping_start);
|
||||||
|
// Map 16 MB or length rounded up to the next MB
|
||||||
|
length = std::min<uint64_t>(std::max<uint64_t>(0x1000000U, (length + 0xFFFFF) & ~0xFFFFF), file_size - mapping_start);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Just map the whole file
|
||||||
|
mapping_start = 0;
|
||||||
|
length = file_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length > std::numeric_limits<size_t>::max())
|
||||||
|
throw std::bad_alloc();
|
||||||
|
|
||||||
|
try {
|
||||||
|
region = agi::util::make_unique<mapped_region>(file, read_only, mapping_start, static_cast<size_t>(length));
|
||||||
|
}
|
||||||
|
catch (interprocess_exception const&) {
|
||||||
|
throw fs::FileSystemUnknownError("Failed mapping a view of the file");
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<char *>(region->get_address()) + offset - mapping_start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <libaegisub/fs_fwd.h>
|
#include <libaegisub/fs_fwd.h>
|
||||||
|
|
||||||
#include <boost/interprocess/detail/os_file_functions.hpp>
|
#include <boost/interprocess/detail/os_file_functions.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace agi {
|
namespace agi {
|
||||||
// boost::interprocess::file_mapping is awesome and uses CreateFileA on Windows
|
// boost::interprocess::file_mapping is awesome and uses CreateFileA on Windows
|
||||||
|
@ -30,4 +31,18 @@ namespace agi {
|
||||||
return boost::interprocess::ipcdetail::mapping_handle_from_file_handle(handle);
|
return boost::interprocess::ipcdetail::mapping_handle_from_file_handle(handle);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class read_file_mapping {
|
||||||
|
file_mapping file;
|
||||||
|
std::unique_ptr<boost::interprocess::mapped_region> region;
|
||||||
|
uint64_t mapping_start = 0;
|
||||||
|
uint64_t file_size = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
read_file_mapping(fs::path const& filename);
|
||||||
|
~read_file_mapping();
|
||||||
|
|
||||||
|
uint64_t size() const { return file_size; }
|
||||||
|
char *read(int64_t offset, uint64_t length);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,56 +44,21 @@
|
||||||
#include <libaegisub/log.h>
|
#include <libaegisub/log.h>
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
#include <boost/interprocess/file_mapping.hpp>
|
|
||||||
#include <boost/interprocess/mapped_region.hpp>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
using namespace boost::interprocess;
|
|
||||||
|
|
||||||
PCMAudioProvider::PCMAudioProvider(agi::fs::path const& filename)
|
PCMAudioProvider::PCMAudioProvider(agi::fs::path const& filename)
|
||||||
: file(agi::util::make_unique<agi::file_mapping>(filename, read_only))
|
: file(agi::util::make_unique<agi::read_file_mapping>(filename))
|
||||||
{
|
{
|
||||||
float_samples = false;
|
float_samples = false;
|
||||||
|
|
||||||
try {
|
|
||||||
file_size = agi::fs::Size(filename);
|
|
||||||
}
|
|
||||||
catch (agi::Exception const& e) {
|
|
||||||
throw agi::AudioPlayerOpenError("Could not get file size", e.Copy());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PCMAudioProvider::~PCMAudioProvider() { }
|
PCMAudioProvider::~PCMAudioProvider() { }
|
||||||
|
|
||||||
char *PCMAudioProvider::EnsureRangeAccessible(int64_t start, int64_t length) const {
|
char *PCMAudioProvider::EnsureRangeAccessible(int64_t start, int64_t length) const {
|
||||||
if (start + length > file_size)
|
|
||||||
throw AudioDecodeError("Attempted to map beyond end of file");
|
|
||||||
|
|
||||||
// Check if we can just use the current mapping
|
|
||||||
if (region && start >= mapping_start && start + length <= mapping_start + region->get_size())
|
|
||||||
return static_cast<char *>(region->get_address()) + start - mapping_start;
|
|
||||||
|
|
||||||
if (sizeof(size_t) == 4) {
|
|
||||||
mapping_start = start & ~0xFFFFFULL; // Align to 1 MB bondary
|
|
||||||
length += static_cast<size_t>(start - mapping_start);
|
|
||||||
// Map 16 MB or length rounded up to the next MB
|
|
||||||
length = std::min<size_t>(std::max<size_t>(0x1000000U, (length + 0xFFFFF) & ~0xFFFFF), file_size - mapping_start);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Just map the whole file
|
|
||||||
mapping_start = 0;
|
|
||||||
length = file_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
region = agi::util::make_unique<mapped_region>(*file, read_only, mapping_start, length);
|
return file->read(start, static_cast<size_t>(length));
|
||||||
}
|
}
|
||||||
catch (interprocess_exception const&) {
|
catch (agi::fs::FileSystemError const& e) {
|
||||||
throw AudioDecodeError("Failed mapping a view of the file");
|
throw AudioDecodeError(e.GetMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_cast<char *>(region->get_address()) + start - mapping_start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
|
void PCMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
|
||||||
|
@ -322,15 +287,13 @@ public:
|
||||||
{
|
{
|
||||||
this->filename = filename;
|
this->filename = filename;
|
||||||
|
|
||||||
int64_t smallest_possible_file = sizeof(RiffChunk) + sizeof(FormatChunk) + sizeof(DataChunk);
|
size_t smallest_possible_file = sizeof(RiffChunk) + sizeof(FormatChunk) + sizeof(DataChunk);
|
||||||
|
|
||||||
if (file_size < smallest_possible_file)
|
if (file->size() < smallest_possible_file)
|
||||||
throw agi::AudioDataNotFoundError("File is too small to be a Wave64 file", nullptr);
|
throw agi::AudioDataNotFoundError("File is too small to be a Wave64 file", nullptr);
|
||||||
|
|
||||||
// Read header
|
// Read header
|
||||||
// This should throw an exception if the mapping fails
|
|
||||||
void *filestart = EnsureRangeAccessible(0, sizeof(RiffChunk));
|
void *filestart = EnsureRangeAccessible(0, sizeof(RiffChunk));
|
||||||
assert(filestart);
|
|
||||||
RiffChunk &header = *(RiffChunk*)filestart;
|
RiffChunk &header = *(RiffChunk*)filestart;
|
||||||
|
|
||||||
// Check magic values
|
// Check magic values
|
||||||
|
|
|
@ -32,22 +32,16 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace agi { class file_mapping; }
|
namespace agi { class read_file_mapping; }
|
||||||
namespace boost { namespace interprocess { class mapped_region; } }
|
|
||||||
|
|
||||||
class PCMAudioProvider : public AudioProvider {
|
class PCMAudioProvider : public AudioProvider {
|
||||||
std::unique_ptr<agi::file_mapping> file;
|
|
||||||
mutable std::unique_ptr<boost::interprocess::mapped_region> region;
|
|
||||||
mutable int64_t mapping_start = 0;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
std::unique_ptr<agi::read_file_mapping> file;
|
||||||
|
|
||||||
PCMAudioProvider(agi::fs::path const& filename); // Create base object and open the file mapping
|
PCMAudioProvider(agi::fs::path const& filename); // Create base object and open the file mapping
|
||||||
~PCMAudioProvider(); // Closes the file mapping
|
~PCMAudioProvider(); // Closes the file mapping
|
||||||
char *EnsureRangeAccessible(int64_t range_start, int64_t range_length) const; // Ensure that the given range of bytes are accessible in the file mapping and return a pointer to the first byte of the requested range
|
char *EnsureRangeAccessible(int64_t range_start, int64_t range_length) const; // Ensure that the given range of bytes are accessible in the file mapping and return a pointer to the first byte of the requested range
|
||||||
|
|
||||||
/// Size of the opened file
|
|
||||||
int64_t file_size = 0;
|
|
||||||
|
|
||||||
struct IndexPoint {
|
struct IndexPoint {
|
||||||
int64_t start_byte;
|
int64_t start_byte;
|
||||||
int64_t start_sample;
|
int64_t start_sample;
|
||||||
|
|
Loading…
Reference in a new issue