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 IntegerToString(int value);
|
||||||
String PrettySize(int bytes);
|
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
|
// Serialize
|
||||||
String DialogueASS::ToText(int version) const
|
String DialogueASS::ToText(int version) const
|
||||||
{
|
{
|
||||||
// Prepare
|
// Old, slow code
|
||||||
wxString final = _T("");
|
if (false) {
|
||||||
|
// Prepare
|
||||||
|
wxString final;
|
||||||
|
|
||||||
// Write comment or dialogue
|
// Write comment or dialogue
|
||||||
if (isComment) final = _T("Comment: ");
|
if (isComment) final = _T("Comment: ");
|
||||||
else final = _T("Dialogue: ");
|
else final = _T("Dialogue: ");
|
||||||
|
|
||||||
// Write layer or marked
|
// Write layer or marked
|
||||||
if (version >= 1) final += wxString::Format(_T("%01i,"),layer);
|
if (version >= 1) final += wxString::Format(_T("%01i,"),layer);
|
||||||
else final += _T("Marked=0,");
|
else final += _T("Marked=0,");
|
||||||
|
|
||||||
// Write times, style and actor
|
// Write times, style and actor
|
||||||
final += start.GetString(2,1) + _T(",") + end.GetString(2,1) + _T(",") + style + _T(",") + actor + _T(",");
|
final += start.GetString(2,1) + _T(",") + end.GetString(2,1) + _T(",") + style + _T(",") + actor + _T(",");
|
||||||
|
|
||||||
// Write margins
|
// Write margins
|
||||||
if (version <= 1) final += wxString::Format(_T("%04i,%04i,%04i,"),margin[0],margin[1],margin[2]);
|
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]);
|
else final += wxString::Format(_T("%04i,%04i,%04i,%04i,"),margin[0],margin[1],margin[2],margin[3]);
|
||||||
|
|
||||||
// Write effect and text
|
// Write effect and text
|
||||||
final += effect + _T(",") + text;
|
final += effect + _T(",") + text;
|
||||||
|
|
||||||
// Make sure that final has no line breaks
|
// Return final
|
||||||
final.Replace(_T("\n"),_T(""));
|
return final;
|
||||||
final.Replace(_T("\r"),_T(""));
|
}
|
||||||
|
|
||||||
// Return final
|
// New, faster code
|
||||||
return final;
|
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
|
// Setup
|
||||||
trim = _trim;
|
trim = _trim;
|
||||||
threaded = prefetch;
|
threaded = prefetch && false;
|
||||||
thread = NULL;
|
thread = NULL;
|
||||||
|
|
||||||
// Set encoding
|
// Set encoding
|
||||||
|
@ -68,6 +68,8 @@ TextFileReader::TextFileReader(wxInputStream &stream,Gorgonsub::String enc,bool
|
||||||
// Destructor
|
// Destructor
|
||||||
TextFileReader::~TextFileReader()
|
TextFileReader::~TextFileReader()
|
||||||
{
|
{
|
||||||
|
wxCriticalSectionLocker locker(mutex);
|
||||||
|
if (thread) thread->Delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -182,6 +184,7 @@ Gorgonsub::String TextFileReader::ActuallyReadLine()
|
||||||
// Checks if there's more to read
|
// Checks if there's more to read
|
||||||
bool TextFileReader::HasMoreLines()
|
bool TextFileReader::HasMoreLines()
|
||||||
{
|
{
|
||||||
|
if (cache.size()) return true;
|
||||||
wxCriticalSectionLocker locker(mutex);
|
wxCriticalSectionLocker locker(mutex);
|
||||||
return (!file.Eof());
|
return (!file.Eof());
|
||||||
}
|
}
|
||||||
|
@ -217,12 +220,13 @@ String TextFileReader::ReadLineFromFile()
|
||||||
|
|
||||||
// Load into cache if needed
|
// Load into cache if needed
|
||||||
String final;
|
String final;
|
||||||
if (cache.size() == 0) {
|
{
|
||||||
wxCriticalSectionLocker locker(mutex);
|
wxCriticalSectionLocker locker(mutex);
|
||||||
cache.push_back(ActuallyReadLine());
|
if (cache.size() == 0) {
|
||||||
|
cache.push_back(ActuallyReadLine());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool startThread = false;
|
|
||||||
{
|
{
|
||||||
// Retrieve from cache
|
// Retrieve from cache
|
||||||
wxCriticalSectionLocker locker(mutex);
|
wxCriticalSectionLocker locker(mutex);
|
||||||
|
@ -234,16 +238,11 @@ String TextFileReader::ReadLineFromFile()
|
||||||
// Start the thread to prefetch more
|
// Start the thread to prefetch more
|
||||||
if (cache.size() < 3 && thread == NULL) {
|
if (cache.size() < 3 && thread == NULL) {
|
||||||
thread = new PrefetchThread(this);
|
thread = new PrefetchThread(this);
|
||||||
startThread = true;
|
thread->Create();
|
||||||
|
thread->Run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starts the thread
|
|
||||||
if (startThread) {
|
|
||||||
thread->Create();
|
|
||||||
thread->Run();
|
|
||||||
}
|
|
||||||
|
|
||||||
return final;
|
return final;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,21 +252,27 @@ String TextFileReader::ReadLineFromFile()
|
||||||
wxThread::ExitCode PrefetchThread::Entry()
|
wxThread::ExitCode PrefetchThread::Entry()
|
||||||
{
|
{
|
||||||
// Lock
|
// Lock
|
||||||
wxCriticalSectionLocker locker(parent->mutex);
|
bool run = true;
|
||||||
while (parent->cache.size() < 3 && !parent->file.Eof()) {
|
while (run) {
|
||||||
// So I need to do this for whatever reason
|
|
||||||
if (TestDestroy()) {
|
if (TestDestroy()) {
|
||||||
parent->thread = NULL;
|
parent->thread = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
// Get line
|
wxCriticalSectionLocker locker(parent->mutex);
|
||||||
parent->cache.push_back(parent->ActuallyReadLine());
|
if (parent->cache.size() < 6) {
|
||||||
|
if (!parent->file.Eof()) {
|
||||||
|
// Get line
|
||||||
|
parent->cache.push_back(parent->ActuallyReadLine());
|
||||||
|
}
|
||||||
|
else run = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Sleep(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Die
|
// Die
|
||||||
//wxCriticalSectionLocker locker(parent->mutex);
|
wxCriticalSectionLocker locker(parent->mutex);
|
||||||
parent->thread = NULL;
|
parent->thread = NULL;
|
||||||
Delete();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ namespace Gorgonsub {
|
||||||
String ActuallyReadLine();
|
String ActuallyReadLine();
|
||||||
|
|
||||||
public:
|
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();
|
~TextFileReader();
|
||||||
|
|
||||||
String ReadLineFromFile();
|
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 == 1) _ms /= 100;
|
||||||
else if (ms_precision == 0) _ms = 0;
|
else if (ms_precision == 0) _ms = 0;
|
||||||
|
|
||||||
// Generate mask string
|
// Old code
|
||||||
wxString mask = wxString::Format(_T("%%0%ii:%%0%ii:%%0%ii.%%0%ii"),h_precision,2,2,ms_precision);
|
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
|
// Generate final string
|
||||||
wxString final = wxString::Format(mask,h,min,s,_ms);
|
wxString final = wxString::Format(mask,h,min,s,_ms);
|
||||||
|
|
||||||
// Done
|
// Done
|
||||||
return final;
|
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) {
|
String Gorgonsub::IntegerToString(int value) {
|
||||||
return wxString::Format(_T("%i"),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_reader.h"
|
||||||
#include "text_file_writer.h"
|
#include "text_file_writer.h"
|
||||||
|
|
||||||
int main () {
|
|
||||||
|
int main() {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace Gorgonsub;
|
using namespace Gorgonsub;
|
||||||
|
|
||||||
|
@ -111,4 +112,6 @@ int main () {
|
||||||
catch (std::exception &e) {
|
catch (std::exception &e) {
|
||||||
cout << "\n\nException: " << e.what() << endl << endl;
|
cout << "\n\nException: " << e.what() << endl << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCCLCompilerTool"
|
Name="VCCLCompilerTool"
|
||||||
AdditionalIncludeDirectories="../include"
|
AdditionalIncludeDirectories="../include"
|
||||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
|
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
|
||||||
RuntimeLibrary="2"
|
RuntimeLibrary="2"
|
||||||
UsePrecompiledHeader="0"
|
UsePrecompiledHeader="0"
|
||||||
WarningLevel="4"
|
WarningLevel="4"
|
||||||
|
|
Loading…
Reference in a new issue