From 6c14c9bee955c9e69506db9efa605cd816ef4b48 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 20 Mar 2014 18:36:29 -0700 Subject: [PATCH] Extract the rest of the mmap logic from the PCM provider. Closes #934. --- libaegisub/common/file_mapping.cpp | 51 +++++++++++++++++++- libaegisub/include/libaegisub/file_mapping.h | 15 ++++++ src/audio_provider_pcm.cpp | 49 +++---------------- src/audio_provider_pcm.h | 12 ++--- 4 files changed, 74 insertions(+), 53 deletions(-) diff --git a/libaegisub/common/file_mapping.cpp b/libaegisub/common/file_mapping.cpp index d661e8e6a..428706fa6 100644 --- a/libaegisub/common/file_mapping.cpp +++ b/libaegisub/common/file_mapping.cpp @@ -22,12 +22,17 @@ #include "libaegisub/util.h" #include +#include +#include + +#ifdef _WIN32 #include +#endif using namespace boost::interprocess; 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 : 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); } } + +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(size); +} + +read_file_mapping::~read_file_mapping() { } + +char *read_file_mapping::read(int64_t s_offset, uint64_t length) { + auto offset = static_cast(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(region->get_address()) + offset - mapping_start; + + if (sizeof(size_t) == 4) { + mapping_start = offset & ~0xFFFFFULL; // Align to 1 MB bondary + length += static_cast(offset - mapping_start); + // Map 16 MB or length rounded up to the next MB + length = std::min(std::max(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::max()) + throw std::bad_alloc(); + + try { + region = agi::util::make_unique(file, read_only, mapping_start, static_cast(length)); + } + catch (interprocess_exception const&) { + throw fs::FileSystemUnknownError("Failed mapping a view of the file"); + } + + return static_cast(region->get_address()) + offset - mapping_start; +} } diff --git a/libaegisub/include/libaegisub/file_mapping.h b/libaegisub/include/libaegisub/file_mapping.h index 6de6d200c..2bff376dc 100644 --- a/libaegisub/include/libaegisub/file_mapping.h +++ b/libaegisub/include/libaegisub/file_mapping.h @@ -17,6 +17,7 @@ #include #include +#include namespace agi { // 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); } }; + + class read_file_mapping { + file_mapping file; + std::unique_ptr 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); + }; } diff --git a/src/audio_provider_pcm.cpp b/src/audio_provider_pcm.cpp index db6a3e63c..b18768084 100644 --- a/src/audio_provider_pcm.cpp +++ b/src/audio_provider_pcm.cpp @@ -44,56 +44,21 @@ #include #include -#include -#include -#include -#include - -using namespace boost::interprocess; - PCMAudioProvider::PCMAudioProvider(agi::fs::path const& filename) -: file(agi::util::make_unique(filename, read_only)) +: file(agi::util::make_unique(filename)) { 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() { } 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(region->get_address()) + start - mapping_start; - - if (sizeof(size_t) == 4) { - mapping_start = start & ~0xFFFFFULL; // Align to 1 MB bondary - length += static_cast(start - mapping_start); - // Map 16 MB or length rounded up to the next MB - length = std::min(std::max(0x1000000U, (length + 0xFFFFF) & ~0xFFFFF), file_size - mapping_start); - } - else { - // Just map the whole file - mapping_start = 0; - length = file_size; - } - try { - region = agi::util::make_unique(*file, read_only, mapping_start, length); + return file->read(start, static_cast(length)); } - catch (interprocess_exception const&) { - throw AudioDecodeError("Failed mapping a view of the file"); + catch (agi::fs::FileSystemError const& e) { + throw AudioDecodeError(e.GetMessage()); } - - return static_cast(region->get_address()) + start - mapping_start; } void PCMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const { @@ -322,15 +287,13 @@ public: { 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); // Read header - // This should throw an exception if the mapping fails void *filestart = EnsureRangeAccessible(0, sizeof(RiffChunk)); - assert(filestart); RiffChunk &header = *(RiffChunk*)filestart; // Check magic values diff --git a/src/audio_provider_pcm.h b/src/audio_provider_pcm.h index 4b2959ac2..e4370a080 100644 --- a/src/audio_provider_pcm.h +++ b/src/audio_provider_pcm.h @@ -32,22 +32,16 @@ #include #include -namespace agi { class file_mapping; } -namespace boost { namespace interprocess { class mapped_region; } } +namespace agi { class read_file_mapping; } class PCMAudioProvider : public AudioProvider { - std::unique_ptr file; - mutable std::unique_ptr region; - mutable int64_t mapping_start = 0; - protected: + std::unique_ptr file; + PCMAudioProvider(agi::fs::path const& filename); // Create base object and open 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 - /// Size of the opened file - int64_t file_size = 0; - struct IndexPoint { int64_t start_byte; int64_t start_sample;