Store ass attachments in the encoded form in memory

This marginally increases memory use, but vastly speeds up pretty much
everything when a file has attachments (other than extracting the
attachments, but that's generally IO-bound anyway).
This commit is contained in:
Thomas Goyne 2013-02-07 09:56:07 -08:00
parent 73217fd0e9
commit a872fc50b8
3 changed files with 53 additions and 96 deletions

View file

@ -1,37 +1,19 @@
// Copyright (c) 2006, Rodrigo Braz Monteiro // Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// //
// * Redistributions of source code must retain the above copyright notice, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
/// @file ass_attachment.cpp
/// @brief Manage files embedded in subtitles
/// @ingroup subs_storage
///
#include "config.h" #include "config.h"
#include "ass_attachment.h" #include "ass_attachment.h"
@ -49,50 +31,52 @@ AssAttachment::AssAttachment(std::string const& name, AssEntryGroup group)
} }
AssAttachment::AssAttachment(agi::fs::path const& name, AssEntryGroup group) AssAttachment::AssAttachment(agi::fs::path const& name, AssEntryGroup group)
: data(new std::vector<char>) : filename(name.filename().string())
, filename(name.filename().string())
, group(group) , group(group)
{ {
if (boost::iends_with(filename, ".ttf")) // SSA stuffs some information about the font in the embeded filename, but
filename = filename.substr(0, filename.size() - 4) + "_0" + filename.substr(filename.size() - 4); // nothing else uses it so just do the absolute minimum (0 is the encoding)
if (boost::iends_with(filename.get(), ".ttf"))
filename = filename.get().substr(0, filename.get().size() - 4) + "_0" + filename.get().substr(filename.get().size() - 4);
std::vector<char> data;
std::unique_ptr<std::istream> file(agi::io::Open(name, true)); std::unique_ptr<std::istream> file(agi::io::Open(name, true));
file->seekg(0, std::ios::end); file->seekg(0, std::ios::end);
data->resize(file->tellg()); data.resize(file->tellg());
file->seekg(0, std::ios::beg); file->seekg(0, std::ios::beg);
file->read(&(*data)[0], data->size()); file->read(&data[0], data.size());
entry_data = (group == ENTRY_FONT ? "fontname: " : "filename: ") + filename.get() + "\r\n";
entry_data = entry_data.get() + agi::ass::UUEncode(data);
} }
AssEntry *AssAttachment::Clone() const { AssEntry *AssAttachment::Clone() const {
AssAttachment *clone = new AssAttachment(filename, group); AssAttachment *clone = new AssAttachment(filename, group);
clone->data = data; clone->entry_data = entry_data;
return clone; return clone;
} }
const std::string AssAttachment::GetEntryData() const { const std::string AssAttachment::GetEntryData() const {
std::string entryData = (group == ENTRY_FONT ? "fontname: " : "filename: ") + filename + "\r\n"; return entry_data;
if (data) }
entryData += agi::ass::UUEncode(*data);
return entryData; size_t AssAttachment::GetSize() const {
auto header_end = entry_data.get().find('\n');
return entry_data.get().size() - header_end - 1;
} }
void AssAttachment::Extract(agi::fs::path const& filename) const { void AssAttachment::Extract(agi::fs::path const& filename) const {
agi::io::Save(filename, true).Get().write(&(*data)[0], data->size()); auto header_end = entry_data.get().find('\n');
agi::io::Save(filename, true).Get().write(&entry_data.get()[header_end + 1], entry_data.get().size() - header_end - 1);
} }
std::string AssAttachment::GetFileName(bool raw) const { std::string AssAttachment::GetFileName(bool raw) const {
if (raw || !boost::iends_with(filename, ".ttf")) return filename; if (raw || !boost::iends_with(filename.get(), ".ttf")) return filename;
// Remove stuff after last underscore if it's a font // Remove stuff after last underscore if it's a font
std::string::size_type last_under = filename.rfind('_'); std::string::size_type last_under = filename.get().rfind('_');
if (last_under == std::string::npos) if (last_under == std::string::npos)
return filename; return filename;
return filename.substr(0, last_under) + ".ttf"; return filename.get().substr(0, last_under) + ".ttf";
}
void AssAttachment::Finish() {
data = std::make_shared<std::vector<char>>(agi::ass::UUDecode(buffer));
buffer.clear();
buffer.shrink_to_fit();
} }

View file

@ -1,66 +1,42 @@
// Copyright (c) 2006, Rodrigo Braz Monteiro // Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// //
// * Redistributions of source code must retain the above copyright notice, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
/// @file ass_attachment.h
/// @see ass_attachment.cpp
/// @ingroup subs_storage
///
#include "ass_entry.h" #include "ass_entry.h"
#include <libaegisub/fs_fwd.h> #include <libaegisub/fs_fwd.h>
#include <memory> #include <boost/flyweight.hpp>
#include <vector> #include <vector>
/// @class AssAttachment /// @class AssAttachment
class AssAttachment : public AssEntry { class AssAttachment : public AssEntry {
/// Decoded file data /// ASS uuencoded entry data, including header.
std::shared_ptr<std::vector<char>> data; boost::flyweight<std::string> entry_data;
/// Encoded data which has been read from the script but not yet decoded
std::string buffer;
/// Name of the attached file, with SSA font mangling if it is a ttf /// Name of the attached file, with SSA font mangling if it is a ttf
std::string filename; boost::flyweight<std::string> filename;
AssEntryGroup group; AssEntryGroup group;
public: public:
/// Get the size of the attached file in bytes /// Get the size of the attached file in bytes
size_t GetSize() const { return data->size(); } size_t GetSize() const;
/// Add a line of data (without newline) read from a subtitle file to the /// Add a line of data (without newline) read from a subtitle file
/// buffer waiting to be decoded void AddData(std::string const& data) { entry_data = entry_data.get() + data + "\r\n"; }
void AddData(std::string const& data) { buffer += data; }
/// Decode all data passed with AddData
void Finish();
/// Extract the contents of this attachment to a file /// Extract the contents of this attachment to a file
/// @param filename Path to save the attachment to /// @param filename Path to save the attachment to

View file

@ -53,7 +53,6 @@ void AssParser::ParseAttachmentLine(std::string const& data) {
// Data is over, add attachment to the file // Data is over, add attachment to the file
if (!valid_data || is_filename) { if (!valid_data || is_filename) {
attach->Finish();
InsertLine(attach.release()); InsertLine(attach.release());
AddLine(data); AddLine(data);
} }
@ -61,11 +60,9 @@ void AssParser::ParseAttachmentLine(std::string const& data) {
attach->AddData(data); attach->AddData(data);
// Done building // Done building
if (data.size() < 80) { if (data.size() < 80)
attach->Finish();
InsertLine(attach.release()); InsertLine(attach.release());
} }
}
} }
void AssParser::ParseScriptInfoLine(std::string const& data) { void AssParser::ParseScriptInfoLine(std::string const& data) {