forked from mia/Aegisub
Increased gorgonsub's ASS write speed with large files 3.5x by getting rid of wxString conversion and concatenation routines and writing my own.
Originally committed to SVN as r2059.
This commit is contained in:
parent
2cbf0e587d
commit
98d5794f20
8 changed files with 188 additions and 48 deletions
|
@ -80,4 +80,16 @@ namespace Gorgonsub {
|
|||
String IntegerToString(int value);
|
||||
String PrettySize(int bytes);
|
||||
|
||||
// Fast string write
|
||||
inline void WriteText(wxChar *&dst,const wxChar *src,size_t len,size_t &pos) {
|
||||
memcpy(dst,src,len*sizeof(wxChar));
|
||||
dst += len;
|
||||
pos += len;
|
||||
}
|
||||
inline void WriteChar(wxChar *&dst,const wxChar &src,size_t &pos) {
|
||||
*dst = src;
|
||||
dst++;
|
||||
pos++;
|
||||
}
|
||||
void WriteNumber(wxChar *&dst,wxChar *temp,int number,int pad,size_t &pos);
|
||||
};
|
||||
|
|
|
@ -140,31 +140,92 @@ bool DialogueASS::Parse(wxString rawData, int version)
|
|||
// Serialize
|
||||
String DialogueASS::ToText(int version) const
|
||||
{
|
||||
// Prepare
|
||||
wxString final = _T("");
|
||||
// Old, slow code
|
||||
if (false) {
|
||||
// Prepare
|
||||
wxString final;
|
||||
|
||||
// Write comment or dialogue
|
||||
if (isComment) final = _T("Comment: ");
|
||||
else final = _T("Dialogue: ");
|
||||
// Write comment or dialogue
|
||||
if (isComment) final = _T("Comment: ");
|
||||
else final = _T("Dialogue: ");
|
||||
|
||||
// Write layer or marked
|
||||
if (version >= 1) final += wxString::Format(_T("%01i,"),layer);
|
||||
else final += _T("Marked=0,");
|
||||
// Write layer or marked
|
||||
if (version >= 1) final += wxString::Format(_T("%01i,"),layer);
|
||||
else final += _T("Marked=0,");
|
||||
|
||||
// Write times, style and actor
|
||||
final += start.GetString(2,1) + _T(",") + end.GetString(2,1) + _T(",") + style + _T(",") + actor + _T(",");
|
||||
// Write times, style and actor
|
||||
final += start.GetString(2,1) + _T(",") + end.GetString(2,1) + _T(",") + style + _T(",") + actor + _T(",");
|
||||
|
||||
// Write margins
|
||||
if (version <= 1) final += wxString::Format(_T("%04i,%04i,%04i,"),margin[0],margin[1],margin[2]);
|
||||
else final += wxString::Format(_T("%04i,%04i,%04i,%04i,"),margin[0],margin[1],margin[2],margin[3]);
|
||||
// Write margins
|
||||
if (version <= 1) final += wxString::Format(_T("%04i,%04i,%04i,"),margin[0],margin[1],margin[2]);
|
||||
else final += wxString::Format(_T("%04i,%04i,%04i,%04i,"),margin[0],margin[1],margin[2],margin[3]);
|
||||
|
||||
// Write effect and text
|
||||
final += effect + _T(",") + text;
|
||||
// Write effect and text
|
||||
final += effect + _T(",") + text;
|
||||
|
||||
// Make sure that final has no line breaks
|
||||
final.Replace(_T("\n"),_T(""));
|
||||
final.Replace(_T("\r"),_T(""));
|
||||
// Return final
|
||||
return final;
|
||||
}
|
||||
|
||||
// Return final
|
||||
return final;
|
||||
// New, faster code
|
||||
else {
|
||||
// Calculate size
|
||||
size_t size = 9+9+20+12+12; // 9 for "comment: " (+1 for dialogue below), 9 for commas,
|
||||
// 20 for times, 12 for margins, 12 just to be sure that layer fits
|
||||
if (!isComment) size++; // Comment->Dialogue
|
||||
if (version == 0) size += 8; // "Marked=0"
|
||||
else if (version == 2) size += 5; // Fourth margin
|
||||
size += style.Length() + actor.Length() + effect.Length() + text.Length();
|
||||
|
||||
// Allocate string
|
||||
wxString final;
|
||||
wxChar *buffer = final.GetWriteBuf(size);
|
||||
wxChar temp[16];
|
||||
|
||||
// Write comment/dialogue
|
||||
size_t pos = 0;
|
||||
if (isComment) WriteText(buffer,_T("Comment: "),9,pos);
|
||||
else WriteText(buffer,_T("Dialogue: "),10,pos);
|
||||
|
||||
// Write layer or marked
|
||||
if (version >= 1) {
|
||||
WriteNumber(buffer,temp,layer,0,pos);
|
||||
WriteChar(buffer,_T(','),pos);
|
||||
}
|
||||
else WriteText(buffer,_T("Marked=0,"),9,pos);
|
||||
|
||||
// Write times
|
||||
wxString tempStr = start.GetString(2,1);
|
||||
WriteText(buffer,&tempStr[0],10,pos);
|
||||
WriteChar(buffer,_T(','),pos);
|
||||
tempStr = end.GetString(2,1);
|
||||
WriteText(buffer,&tempStr[0],10,pos);
|
||||
WriteChar(buffer,_T(','),pos);
|
||||
|
||||
// Write style and actor
|
||||
WriteText(buffer,&style[0],style.Length(),pos);
|
||||
WriteChar(buffer,_T(','),pos);
|
||||
WriteText(buffer,&actor[0],actor.Length(),pos);
|
||||
WriteChar(buffer,_T(','),pos);
|
||||
|
||||
// Write margins
|
||||
size_t marCount = 3;
|
||||
if (version == 2) marCount++;
|
||||
for (size_t i=0;i<marCount;i++) {
|
||||
WriteNumber(buffer,temp,margin[i],4,pos);
|
||||
WriteChar(buffer,_T(','),pos);
|
||||
}
|
||||
|
||||
// Write effect and text
|
||||
WriteText(buffer,&effect[0],effect.Length(),pos);
|
||||
WriteChar(buffer,_T(','),pos);
|
||||
WriteText(buffer,&text[0],text.Length(),pos);
|
||||
|
||||
// Write terminator
|
||||
WriteText(buffer,_T("\0"),1,pos);
|
||||
|
||||
// Restore string's state
|
||||
final.UngetWriteBuf(pos-1);
|
||||
return final;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ TextFileReader::TextFileReader(wxInputStream &stream,Gorgonsub::String enc,bool
|
|||
{
|
||||
// Setup
|
||||
trim = _trim;
|
||||
threaded = prefetch;
|
||||
threaded = prefetch && false;
|
||||
thread = NULL;
|
||||
|
||||
// Set encoding
|
||||
|
@ -68,6 +68,8 @@ TextFileReader::TextFileReader(wxInputStream &stream,Gorgonsub::String enc,bool
|
|||
// Destructor
|
||||
TextFileReader::~TextFileReader()
|
||||
{
|
||||
wxCriticalSectionLocker locker(mutex);
|
||||
if (thread) thread->Delete();
|
||||
}
|
||||
|
||||
|
||||
|
@ -182,6 +184,7 @@ Gorgonsub::String TextFileReader::ActuallyReadLine()
|
|||
// Checks if there's more to read
|
||||
bool TextFileReader::HasMoreLines()
|
||||
{
|
||||
if (cache.size()) return true;
|
||||
wxCriticalSectionLocker locker(mutex);
|
||||
return (!file.Eof());
|
||||
}
|
||||
|
@ -217,12 +220,13 @@ String TextFileReader::ReadLineFromFile()
|
|||
|
||||
// Load into cache if needed
|
||||
String final;
|
||||
if (cache.size() == 0) {
|
||||
{
|
||||
wxCriticalSectionLocker locker(mutex);
|
||||
cache.push_back(ActuallyReadLine());
|
||||
if (cache.size() == 0) {
|
||||
cache.push_back(ActuallyReadLine());
|
||||
}
|
||||
}
|
||||
|
||||
bool startThread = false;
|
||||
{
|
||||
// Retrieve from cache
|
||||
wxCriticalSectionLocker locker(mutex);
|
||||
|
@ -234,16 +238,11 @@ String TextFileReader::ReadLineFromFile()
|
|||
// Start the thread to prefetch more
|
||||
if (cache.size() < 3 && thread == NULL) {
|
||||
thread = new PrefetchThread(this);
|
||||
startThread = true;
|
||||
thread->Create();
|
||||
thread->Run();
|
||||
}
|
||||
}
|
||||
|
||||
// Starts the thread
|
||||
if (startThread) {
|
||||
thread->Create();
|
||||
thread->Run();
|
||||
}
|
||||
|
||||
return final;
|
||||
}
|
||||
|
||||
|
@ -253,21 +252,27 @@ String TextFileReader::ReadLineFromFile()
|
|||
wxThread::ExitCode PrefetchThread::Entry()
|
||||
{
|
||||
// Lock
|
||||
wxCriticalSectionLocker locker(parent->mutex);
|
||||
while (parent->cache.size() < 3 && !parent->file.Eof()) {
|
||||
// So I need to do this for whatever reason
|
||||
bool run = true;
|
||||
while (run) {
|
||||
if (TestDestroy()) {
|
||||
parent->thread = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get line
|
||||
parent->cache.push_back(parent->ActuallyReadLine());
|
||||
{
|
||||
wxCriticalSectionLocker locker(parent->mutex);
|
||||
if (parent->cache.size() < 6) {
|
||||
if (!parent->file.Eof()) {
|
||||
// Get line
|
||||
parent->cache.push_back(parent->ActuallyReadLine());
|
||||
}
|
||||
else run = false;
|
||||
}
|
||||
}
|
||||
Sleep(50);
|
||||
}
|
||||
|
||||
// Die
|
||||
//wxCriticalSectionLocker locker(parent->mutex);
|
||||
wxCriticalSectionLocker locker(parent->mutex);
|
||||
parent->thread = NULL;
|
||||
Delete();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ namespace Gorgonsub {
|
|||
String ActuallyReadLine();
|
||||
|
||||
public:
|
||||
TextFileReader(wxInputStream &stream,String encoding=_T(""),bool trim=true,bool prefetch=false);
|
||||
TextFileReader(wxInputStream &stream,String encoding=_T(""),bool trim=true,bool prefetch=true);
|
||||
~TextFileReader();
|
||||
|
||||
String ReadLineFromFile();
|
||||
|
|
|
@ -67,14 +67,43 @@ String Time::GetString(int ms_precision,int h_precision) const
|
|||
else if (ms_precision == 1) _ms /= 100;
|
||||
else if (ms_precision == 0) _ms = 0;
|
||||
|
||||
// Generate mask string
|
||||
wxString mask = wxString::Format(_T("%%0%ii:%%0%ii:%%0%ii.%%0%ii"),h_precision,2,2,ms_precision);
|
||||
// Old code
|
||||
if (false) {
|
||||
// Generate mask string
|
||||
wxString mask = wxString::Format(_T("%%0%ii:%%0%ii:%%0%ii.%%0%ii"),h_precision,2,2,ms_precision);
|
||||
|
||||
// Generate final string
|
||||
wxString final = wxString::Format(mask,h,min,s,_ms);
|
||||
// Generate final string
|
||||
wxString final = wxString::Format(mask,h,min,s,_ms);
|
||||
|
||||
// Done
|
||||
return final;
|
||||
// Done
|
||||
return final;
|
||||
}
|
||||
|
||||
// New code
|
||||
else {
|
||||
// Get write buffer
|
||||
wxString final;
|
||||
size_t size = 7+h_precision+ms_precision;
|
||||
size_t pos = 0;
|
||||
wxChar *buffer = final.GetWriteBuf(size);
|
||||
wxChar temp[16];
|
||||
|
||||
// Write time
|
||||
WriteNumber(buffer,temp,h,h_precision,pos);
|
||||
WriteChar(buffer,_T(':'),pos);
|
||||
WriteNumber(buffer,temp,min,2,pos);
|
||||
WriteChar(buffer,_T(':'),pos);
|
||||
WriteNumber(buffer,temp,s,2,pos);
|
||||
WriteChar(buffer,_T('.'),pos);
|
||||
WriteNumber(buffer,temp,_ms,ms_precision,pos);
|
||||
|
||||
// Write terminator
|
||||
WriteText(buffer,_T("\0"),1,pos);
|
||||
|
||||
// Restore string's state and return
|
||||
final.UngetWriteBuf(pos-1);
|
||||
return final;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -82,3 +82,33 @@ String Gorgonsub::FloatToString(double value) {
|
|||
String Gorgonsub::IntegerToString(int value) {
|
||||
return wxString::Format(_T("%i"),value);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////
|
||||
// Fast writing to a string
|
||||
void Gorgonsub::WriteNumber(wxChar *&dst,wxChar *temp,int number,int pad,size_t &pos) {
|
||||
// Write number backwards first
|
||||
int div, value;
|
||||
size_t len;
|
||||
for (len=0;true;len++) {
|
||||
div = number / 10;
|
||||
value = number - (div*10);
|
||||
temp[len] = (wxChar) (value + '0');
|
||||
if (!div) break;
|
||||
number = div;
|
||||
}
|
||||
len++;
|
||||
|
||||
// Pad with zeroes
|
||||
pad -= (int)len;
|
||||
for (int i=0;i<pad;i++) {
|
||||
*dst++ = (wxChar) '0';
|
||||
pos++;
|
||||
}
|
||||
|
||||
// Write number
|
||||
for (size_t i=0;i<len;i++) {
|
||||
*dst++ = temp[len-i-1];
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@
|
|||
#include "text_file_reader.h"
|
||||
#include "text_file_writer.h"
|
||||
|
||||
int main () {
|
||||
|
||||
int main() {
|
||||
using namespace std;
|
||||
using namespace Gorgonsub;
|
||||
|
||||
|
@ -111,4 +112,6 @@ int main () {
|
|||
catch (std::exception &e) {
|
||||
cout << "\n\nException: " << e.what() << endl << endl;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@
|
|||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories="../include"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
|
|
Loading…
Reference in a new issue