* Make all requests work asyncronously for Dsl, Bgl and Stardict file formats.

This commit is contained in:
Konstantin Isakov 2009-04-16 11:33:12 +00:00
parent 273429efe1
commit c79e91d8a3
3 changed files with 762 additions and 220 deletions

View file

@ -15,6 +15,10 @@
#include <ctype.h>
#include <string.h>
#include <QSemaphore>
#include <QThreadPool>
#include <QAtomicInt>
namespace Bgl {
using std::map;
@ -217,7 +221,11 @@ namespace
void loadArticle( uint32_t offset, string & headword,
string & displayedHeadword, string & articleText );
void replaceCharsetEntities( string & );
static void replaceCharsetEntities( string & );
friend class BglHeadwordsRequest;
friend class BglArticleRequest;
friend class BglResourceRequest;
};
BglDictionary::BglDictionary( string const & id, string const & indexFile,
@ -266,223 +274,480 @@ namespace
displayedHeadword.size() + 2 );
}
sptr< Dictionary::WordSearchRequest >
BglDictionary::findHeadwordsForSynonym( wstring const & str )
throw( std::exception )
/// BglDictionary::findHeadwordsForSynonym()
class BglHeadwordsRequest;
class BglHeadwordsRequestRunnable: public QRunnable
{
BglHeadwordsRequest & r;
QSemaphore & hasExited;
public:
BglHeadwordsRequestRunnable( BglHeadwordsRequest & r_,
QSemaphore & hasExited_ ): r( r_ ),
hasExited( hasExited_ )
{}
~BglHeadwordsRequestRunnable()
{
sptr< Dictionary::WordSearchRequestInstant > result =
new Dictionary::WordSearchRequestInstant;
hasExited.release();
}
virtual void run();
};
vector< WordArticleLink > chain = findArticles( str );
class BglHeadwordsRequest: public Dictionary::WordSearchRequest
{
friend class BglHeadwordsRequestRunnable;
wstring caseFolded = Folding::applySimpleCaseOnly( str );
wstring str;
BglDictionary & dict;
for( unsigned x = 0; x < chain.size(); ++x )
{
string headword, displayedHeadword, articleText;
QAtomicInt isCancelled;
QSemaphore hasExited;
loadArticle( chain[ x ].articleOffset,
headword, displayedHeadword, articleText );
public:
wstring headwordDecoded = Utf8::decode( removePostfix( headword ) );
if ( caseFolded != Folding::applySimpleCaseOnly( headwordDecoded ) )
{
// The headword seems to differ from the input word, which makes the
// input word its synonym.
result->getMatches().push_back( headwordDecoded );
}
}
return result;
BglHeadwordsRequest( wstring const & word_,
BglDictionary & dict_ ):
str( word_ ), dict( dict_ )
{
QThreadPool::globalInstance()->start(
new BglHeadwordsRequestRunnable( *this, hasExited ) );
}
// Converts a $1$-like postfix to a <sup>1</sup> one
string postfixToSuperscript( string const & in )
{
if ( !in.size() || in[ in.size() - 1 ] != '$' )
return in;
void run(); // Run from another thread by BglHeadwordsRequestRunnable
for( long x = in.size() - 2; x >= 0; x-- )
virtual void cancel()
{
isCancelled.ref();
}
~BglHeadwordsRequest()
{
isCancelled.ref();
hasExited.acquire();
}
};
void BglHeadwordsRequestRunnable::run()
{
r.run();
}
void BglHeadwordsRequest::run()
{
if ( isCancelled )
{
finish();
return;
}
sptr< Dictionary::WordSearchRequestInstant > result =
new Dictionary::WordSearchRequestInstant;
vector< WordArticleLink > chain = dict.findArticles( str );
wstring caseFolded = Folding::applySimpleCaseOnly( str );
for( unsigned x = 0; x < chain.size(); ++x )
{
if ( isCancelled )
{
if ( in[ x ] == '$' )
finish();
return;
}
string headword, displayedHeadword, articleText;
dict.loadArticle( chain[ x ].articleOffset,
headword, displayedHeadword, articleText );
wstring headwordDecoded = Utf8::decode( removePostfix( headword ) );
if ( caseFolded != Folding::applySimpleCaseOnly( headwordDecoded ) )
{
// The headword seems to differ from the input word, which makes the
// input word its synonym.
Mutex::Lock _( dataMutex );
matches.push_back( headwordDecoded );
}
}
finish();
}
sptr< Dictionary::WordSearchRequest >
BglDictionary::findHeadwordsForSynonym( wstring const & word )
throw( std::exception )
{
return new BglHeadwordsRequest( word, *this );
}
// Converts a $1$-like postfix to a <sup>1</sup> one
string postfixToSuperscript( string const & in )
{
if ( !in.size() || in[ in.size() - 1 ] != '$' )
return in;
for( long x = in.size() - 2; x >= 0; x-- )
{
if ( in[ x ] == '$' )
{
if ( in.size() - x - 2 > 2 )
{
if ( in.size() - x - 2 > 2 )
{
// Large postfixes seem like something we wouldn't want to show --
// some dictionaries seem to have each word numbered using the
// postfix.
return in.substr( 0, x );
}
else
return in.substr( 0, x ) + "<sup>" + in.substr( x + 1, in.size() - x - 2 ) + "</sup>";
// Large postfixes seem like something we wouldn't want to show --
// some dictionaries seem to have each word numbered using the
// postfix.
return in.substr( 0, x );
}
else
if ( !isdigit( in[ x ] ) )
break;
return in.substr( 0, x ) + "<sup>" + in.substr( x + 1, in.size() - x - 2 ) + "</sup>";
}
return in;
else
if ( !isdigit( in[ x ] ) )
break;
}
sptr< Dictionary::DataRequest > BglDictionary::getArticle( wstring const & word,
vector< wstring > const & alts )
throw( std::exception )
return in;
}
/// BglDictionary::getArticle()
class BglArticleRequest;
class BglArticleRequestRunnable: public QRunnable
{
BglArticleRequest & r;
QSemaphore & hasExited;
public:
BglArticleRequestRunnable( BglArticleRequest & r_,
QSemaphore & hasExited_ ): r( r_ ),
hasExited( hasExited_ )
{}
~BglArticleRequestRunnable()
{
vector< WordArticleLink > chain = findArticles( word );
hasExited.release();
}
virtual void run();
};
for( unsigned x = 0; x < alts.size(); ++x )
{
/// Make an additional query for each alt
class BglArticleRequest: public Dictionary::DataRequest
{
friend class BglArticleRequestRunnable;
vector< WordArticleLink > altChain = findArticles( alts[ x ] );
wstring word;
vector< wstring > alts;
BglDictionary & dict;
chain.insert( chain.end(), altChain.begin(), altChain.end() );
}
QAtomicInt isCancelled;
QSemaphore hasExited;
multimap< wstring, pair< string, string > > mainArticles, alternateArticles;
public:
set< uint32_t > articlesIncluded; // Some synonims make it that the articles
// appear several times. We combat this
// by only allowing them to appear once.
wstring wordCaseFolded = Folding::applySimpleCaseOnly( word );
for( unsigned x = 0; x < chain.size(); ++x )
{
if ( articlesIncluded.find( chain[ x ].articleOffset ) != articlesIncluded.end() )
continue; // We already have this article in the body.
// Now grab that article
string headword, displayedHeadword, articleText;
loadArticle( chain[ x ].articleOffset,
headword, displayedHeadword, articleText );
// Ok. Now, does it go to main articles, or to alternate ones? We list
// main ones first, and alternates after.
// We do the case-folded and postfix-less comparison here.
wstring headwordStripped =
Folding::applySimpleCaseOnly( Utf8::decode( removePostfix( headword ) ) );
multimap< wstring, pair< string, string > > & mapToUse =
( wordCaseFolded == headwordStripped ) ?
mainArticles : alternateArticles;
mapToUse.insert( pair< wstring, pair< string, string > >(
Folding::applySimpleCaseOnly( Utf8::decode( headword ) ),
pair< string, string >(
displayedHeadword.size() ? displayedHeadword : headword,
articleText ) ) );
articlesIncluded.insert( chain[ x ].articleOffset );
}
if ( mainArticles.empty() && alternateArticles.empty() )
return new Dictionary::DataRequestInstant( false ); // No such word
string result;
multimap< wstring, pair< string, string > >::const_iterator i;
string cleaner = "</font>""</font>""</font>""</font>""</font>""</font>"
"</font>""</font>""</font>""</font>""</font>""</font>"
"</b></b></b></b></b></b></b></b>"
"</i></i></i></i></i></i></i></i>";
for( i = mainArticles.begin(); i != mainArticles.end(); ++i )
{
result += "<h3>";
result += postfixToSuperscript( i->second.first );
result += "</h3>";
result += i->second.second;
result += cleaner;
}
for( i = alternateArticles.begin(); i != alternateArticles.end(); ++i )
{
result += "<h3>";
result += postfixToSuperscript( i->second.first );
result += "</h3>";
result += i->second.second;
result += cleaner;
}
// Do some cleanups in the text
replaceCharsetEntities( result );
Dictionary::DataRequestInstant * ret =
new Dictionary::DataRequestInstant( true );
ret->getData().resize( result.size() );
memcpy( &(ret->getData().front()), result.data(), result.size() );
return ret;
BglArticleRequest( wstring const & word_,
vector< wstring > const & alts_,
BglDictionary & dict_ ):
word( word_ ), alts( alts_ ), dict( dict_ )
{
QThreadPool::globalInstance()->start(
new BglArticleRequestRunnable( *this, hasExited ) );
}
sptr< Dictionary::DataRequest > BglDictionary::getResource( string const & name )
throw( std::exception )
void run(); // Run from another thread by BglArticleRequestRunnable
virtual void cancel()
{
string nameLowercased = name;
isCancelled.ref();
}
~BglArticleRequest()
{
isCancelled.ref();
hasExited.acquire();
}
};
for( string::iterator i = nameLowercased.begin(); i != nameLowercased.end();
++i )
*i = tolower( *i );
void BglArticleRequestRunnable::run()
{
r.run();
}
Mutex::Lock _( idxMutex );
void BglArticleRequest::run()
{
if ( isCancelled )
{
finish();
return;
}
idx.seek( idxHeader.resourceListOffset );
vector< WordArticleLink > chain = dict.findArticles( word );
for( size_t count = idxHeader.resourcesCount; count--; )
for( unsigned x = 0; x < alts.size(); ++x )
{
/// Make an additional query for each alt
vector< WordArticleLink > altChain = dict.findArticles( alts[ x ] );
chain.insert( chain.end(), altChain.begin(), altChain.end() );
}
multimap< wstring, pair< string, string > > mainArticles, alternateArticles;
set< uint32_t > articlesIncluded; // Some synonims make it that the articles
// appear several times. We combat this
// by only allowing them to appear once.
wstring wordCaseFolded = Folding::applySimpleCaseOnly( word );
for( unsigned x = 0; x < chain.size(); ++x )
{
if ( isCancelled )
{
vector< char > nameData( idx.read< uint32_t >() );
idx.read( &nameData.front(), nameData.size() );
finish();
return;
}
for( size_t x = nameData.size(); x--; )
nameData[ x ] = tolower( nameData[ x ] );
if ( articlesIncluded.find( chain[ x ].articleOffset ) != articlesIncluded.end() )
continue; // We already have this article in the body.
uint32_t offset = idx.read< uint32_t >();
// Now grab that article
if ( string( &nameData.front(), nameData.size() ) == nameLowercased )
string headword, displayedHeadword, articleText;
dict.loadArticle( chain[ x ].articleOffset,
headword, displayedHeadword, articleText );
// Ok. Now, does it go to main articles, or to alternate ones? We list
// main ones first, and alternates after.
// We do the case-folded and postfix-less comparison here.
wstring headwordStripped =
Folding::applySimpleCaseOnly( Utf8::decode( removePostfix( headword ) ) );
multimap< wstring, pair< string, string > > & mapToUse =
( wordCaseFolded == headwordStripped ) ?
mainArticles : alternateArticles;
mapToUse.insert( pair< wstring, pair< string, string > >(
Folding::applySimpleCaseOnly( Utf8::decode( headword ) ),
pair< string, string >(
displayedHeadword.size() ? displayedHeadword : headword,
articleText ) ) );
articlesIncluded.insert( chain[ x ].articleOffset );
}
if ( mainArticles.empty() && alternateArticles.empty() )
{
// No such word
finish();
return;
}
string result;
multimap< wstring, pair< string, string > >::const_iterator i;
string cleaner = "</font>""</font>""</font>""</font>""</font>""</font>"
"</font>""</font>""</font>""</font>""</font>""</font>"
"</b></b></b></b></b></b></b></b>"
"</i></i></i></i></i></i></i></i>";
for( i = mainArticles.begin(); i != mainArticles.end(); ++i )
{
result += "<h3>";
result += postfixToSuperscript( i->second.first );
result += "</h3>";
result += i->second.second;
result += cleaner;
}
for( i = alternateArticles.begin(); i != alternateArticles.end(); ++i )
{
result += "<h3>";
result += postfixToSuperscript( i->second.first );
result += "</h3>";
result += i->second.second;
result += cleaner;
}
// Do some cleanups in the text
BglDictionary::replaceCharsetEntities( result );
Mutex::Lock _( dataMutex );
data.resize( result.size() );
memcpy( &data.front(), result.data(), result.size() );
hasAnyData = true;
finish();
}
sptr< Dictionary::DataRequest > BglDictionary::getArticle( wstring const & word,
vector< wstring > const & alts )
throw( std::exception )
{
return new BglArticleRequest( word, alts, *this );
}
//// BglDictionary::getResource()
class BglResourceRequest;
class BglResourceRequestRunnable: public QRunnable
{
BglResourceRequest & r;
QSemaphore & hasExited;
public:
BglResourceRequestRunnable( BglResourceRequest & r_,
QSemaphore & hasExited_ ): r( r_ ),
hasExited( hasExited_ )
{}
~BglResourceRequestRunnable()
{
hasExited.release();
}
virtual void run();
};
class BglResourceRequest: public Dictionary::DataRequest
{
friend class BglResourceRequestRunnable;
Mutex & idxMutex;
File::Class & idx;
uint32_t resourceListOffset, resourcesCount;
string name;
QAtomicInt isCancelled;
QSemaphore hasExited;
public:
BglResourceRequest( Mutex & idxMutex_,
File::Class & idx_,
uint32_t resourceListOffset_,
uint32_t resourcesCount_,
string const & name_ ):
idxMutex( idxMutex_ ),
idx( idx_ ),
resourceListOffset( resourceListOffset_ ),
resourcesCount( resourcesCount_ ),
name( name_ )
{
QThreadPool::globalInstance()->start(
new BglResourceRequestRunnable( *this, hasExited ) );
}
void run(); // Run from another thread by BglResourceRequestRunnable
virtual void cancel()
{
isCancelled.ref();
}
~BglResourceRequest()
{
isCancelled.ref();
hasExited.acquire();
}
};
void BglResourceRequestRunnable::run()
{
r.run();
}
void BglResourceRequest::run()
{
if ( isCancelled )
{
finish();
return;
}
string nameLowercased = name;
for( string::iterator i = nameLowercased.begin(); i != nameLowercased.end();
++i )
*i = tolower( *i );
Mutex::Lock _( idxMutex );
idx.seek( resourceListOffset );
for( size_t count = resourcesCount; count--; )
{
if ( isCancelled )
break;
vector< char > nameData( idx.read< uint32_t >() );
idx.read( &nameData.front(), nameData.size() );
for( size_t x = nameData.size(); x--; )
nameData[ x ] = tolower( nameData[ x ] );
uint32_t offset = idx.read< uint32_t >();
if ( string( &nameData.front(), nameData.size() ) == nameLowercased )
{
// We have a match.
idx.seek( offset );
Mutex::Lock _( dataMutex );
data.resize( idx.read< uint32_t >() );
vector< unsigned char > compressedData( idx.read< uint32_t >() );
idx.read( &compressedData.front(), compressedData.size() );
unsigned long decompressedLength = data.size();
if ( uncompress( (unsigned char *) &data.front(),
&decompressedLength,
&compressedData.front(),
compressedData.size() ) != Z_OK ||
decompressedLength != data.size() )
{
// We have a match.
idx.seek( offset );
sptr< Dictionary::DataRequestInstant > result = new
Dictionary::DataRequestInstant( true );
result->getData().resize( idx.read< uint32_t >() );
vector< unsigned char > compressedData( idx.read< uint32_t >() );
idx.read( &compressedData.front(), compressedData.size() );
unsigned long decompressedLength = result->getData().size();
if ( uncompress( (unsigned char *) &( result->getData().front() ),
&decompressedLength,
&compressedData.front(),
compressedData.size() ) != Z_OK ||
decompressedLength != result->getData().size() )
{
printf( "Failed to decompress resource %s, ignoring it.\n",
name.c_str() );
return new Dictionary::DataRequestInstant( false );
}
return result;
printf( "Failed to decompress resource %s, ignoring it.\n",
name.c_str() );
}
}
else
hasAnyData = true;
return new Dictionary::DataRequestInstant( false );
break;
}
}
finish();
}
sptr< Dictionary::DataRequest > BglDictionary::getResource( string const & name )
throw( std::exception )
{
return new BglResourceRequest( idxMutex, idx, idxHeader.resourceListOffset,
idxHeader.resourcesCount, name );
}
/// Replaces <CHARSET c="t">1234;</CHARSET> occurences with &#x1234;
void BglDictionary::replaceCharsetEntities( string & text )
{

View file

@ -90,6 +90,7 @@ class DslDictionary: public BtreeIndexing::BtreeDictionary
ChunkedStorage::Reader chunks;
string dictionaryName;
map< string, string > abrv;
Mutex dzMutex;
dictData * dz;
Mutex resourceZipMutex;
zip * resourceZip;
@ -142,6 +143,8 @@ private:
// Parts of dslToHtml()
string nodeToHtml( ArticleDom::Node const & );
string processNodeChildren( ArticleDom::Node const & node );
friend class DslArticleRequest;
};
DslDictionary::DslDictionary( string const & id,
@ -246,7 +249,14 @@ void DslDictionary::loadArticle( uint32_t address,
printf( "offset = %x\n", articleOffset );
char * articleBody = dict_data_read_( dz, articleOffset, articleSize, 0, 0 );
char * articleBody;
{
Mutex::Lock _( dzMutex );
articleBody = dict_data_read_( dz, articleOffset, articleSize, 0, 0 );
}
if ( !articleBody )
throw exCantReadFile( getDictionaryFilenames()[ 0 ] );
@ -569,18 +579,86 @@ vector< wstring > StardictDictionary::findHeadwordsForSynonym( wstring const & s
}
#endif
/// DslDictionary::getArticle()
sptr< Dictionary::DataRequest > DslDictionary::getArticle( wstring const & word,
vector< wstring > const & alts )
throw( std::exception )
class DslArticleRequest;
class DslArticleRequestRunnable: public QRunnable
{
vector< WordArticleLink > chain = findArticles( word );
DslArticleRequest & r;
QSemaphore & hasExited;
public:
DslArticleRequestRunnable( DslArticleRequest & r_,
QSemaphore & hasExited_ ): r( r_ ),
hasExited( hasExited_ )
{}
~DslArticleRequestRunnable()
{
hasExited.release();
}
virtual void run();
};
class DslArticleRequest: public Dictionary::DataRequest
{
friend class DslArticleRequestRunnable;
wstring word;
vector< wstring > alts;
DslDictionary & dict;
QAtomicInt isCancelled;
QSemaphore hasExited;
public:
DslArticleRequest( wstring const & word_,
vector< wstring > const & alts_,
DslDictionary & dict_ ):
word( word_ ), alts( alts_ ), dict( dict_ )
{
QThreadPool::globalInstance()->start(
new DslArticleRequestRunnable( *this, hasExited ) );
}
void run(); // Run from another thread by DslArticleRequestRunnable
virtual void cancel()
{
isCancelled.ref();
}
~DslArticleRequest()
{
isCancelled.ref();
hasExited.acquire();
}
};
void DslArticleRequestRunnable::run()
{
r.run();
}
void DslArticleRequest::run()
{
if ( isCancelled )
{
finish();
return;
}
vector< WordArticleLink > chain = dict.findArticles( word );
for( unsigned x = 0; x < alts.size(); ++x )
{
/// Make an additional query for each alt
vector< WordArticleLink > altChain = findArticles( alts[ x ] );
vector< WordArticleLink > altChain = dict.findArticles( alts[ x ] );
chain.insert( chain.end(), altChain.begin(), altChain.end() );
}
@ -595,6 +673,13 @@ sptr< Dictionary::DataRequest > DslDictionary::getArticle( wstring const & word,
for( unsigned x = 0; x < chain.size(); ++x )
{
// Check if we're cancelled occasionally
if ( isCancelled )
{
finish();
return;
}
if ( articlesIncluded.find( chain[ x ].articleOffset ) != articlesIncluded.end() )
continue; // We already have this article in the body.
@ -605,8 +690,8 @@ sptr< Dictionary::DataRequest > DslDictionary::getArticle( wstring const & word,
list< wstring > displayedHeadwords;
wstring articleBody;
loadArticle( chain[ x ].articleOffset, headword, displayedHeadwords,
articleBody );
dict.loadArticle( chain[ x ].articleOffset, headword, displayedHeadwords,
articleBody );
string articleText;
@ -615,7 +700,7 @@ sptr< Dictionary::DataRequest > DslDictionary::getArticle( wstring const & word,
for( list< wstring >::const_iterator i = displayedHeadwords.begin();
i != displayedHeadwords.end(); ++i )
articleText += dslToHtml( *i );
articleText += dict.dslToHtml( *i );
articleText += "</div>";
@ -623,10 +708,10 @@ sptr< Dictionary::DataRequest > DslDictionary::getArticle( wstring const & word,
expandTildes( articleBody, displayedHeadwords.front() );
articleText += "<div class=\"dsl_definition\">";
articleText += dslToHtml( articleBody );
articleText += dict.dslToHtml( articleBody );
articleText += "</div>";
articleText += "</span>";
// Ok. Now, does it go to main articles, or to alternate ones? We list
// main ones first, and alternates after.
@ -647,7 +732,10 @@ sptr< Dictionary::DataRequest > DslDictionary::getArticle( wstring const & word,
}
if ( mainArticles.empty() && alternateArticles.empty() )
return new Dictionary::DataRequestInstant( false );
{
finish();
return;
}
string result;
@ -659,14 +747,22 @@ sptr< Dictionary::DataRequest > DslDictionary::getArticle( wstring const & word,
for( i = alternateArticles.begin(); i != alternateArticles.end(); ++i )
result += i->second;
Dictionary::DataRequestInstant * ret =
new Dictionary::DataRequestInstant( true );
Mutex::Lock _( dataMutex );
ret->getData().resize( result.size() );
data.resize( result.size() );
memcpy( &(ret->getData().front()), result.data(), result.size() );
memcpy( &data.front(), result.data(), result.size() );
return ret;
hasAnyData = true;
finish();
}
sptr< Dictionary::DataRequest > DslDictionary::getArticle( wstring const & word,
vector< wstring > const & alts )
throw( std::exception )
{
return new DslArticleRequest( word, alts, *this );
}
void loadFromFile( string const & n, vector< char > & data )

View file

@ -21,6 +21,9 @@
#include <stdlib.h>
#include <QString>
#include <QSemaphore>
#include <QThreadPool>
#include <QAtomicInt>
namespace Stardict {
@ -102,6 +105,7 @@ class StardictDictionary: public BtreeIndexing::BtreeDictionary
string bookName;
string sameTypeSequence;
ChunkedStorage::Reader chunks;
Mutex dzMutex;
dictData * dz;
public:
@ -144,6 +148,9 @@ private:
string & articleText );
string loadString( size_t size );
friend class StardictArticleRequest;
friend class StardictHeadwordsRequest;
};
StardictDictionary::StardictDictionary( string const & id,
@ -259,8 +266,14 @@ void StardictDictionary::loadArticle( uint32_t address,
getArticleProps( address, headword, offset, size );
// Note that the function always zero-pads the result.
char * articleBody = dict_data_read_( dz, offset, size, 0, 0 );
char * articleBody;
{
Mutex::Lock _( dzMutex );
// Note that the function always zero-pads the result.
articleBody = dict_data_read_( dz, offset, size, 0, 0 );
}
if ( !articleBody )
throw exCantReadFile( getDictionaryFilenames()[ 2 ] );
@ -411,22 +424,94 @@ void StardictDictionary::loadArticle( uint32_t address,
free( articleBody );
}
sptr< Dictionary::WordSearchRequest > StardictDictionary::findHeadwordsForSynonym( wstring const & str )
throw( std::exception )
/// StardictDictionary::findHeadwordsForSynonym()
class StardictHeadwordsRequest;
class StardictHeadwordsRequestRunnable: public QRunnable
{
sptr< Dictionary::WordSearchRequestInstant > result =
new Dictionary::WordSearchRequestInstant;
StardictHeadwordsRequest & r;
QSemaphore & hasExited;
public:
vector< WordArticleLink > chain = findArticles( str );
StardictHeadwordsRequestRunnable( StardictHeadwordsRequest & r_,
QSemaphore & hasExited_ ): r( r_ ),
hasExited( hasExited_ )
{}
wstring caseFolded = Folding::applySimpleCaseOnly( str );
~StardictHeadwordsRequestRunnable()
{
hasExited.release();
}
virtual void run();
};
class StardictHeadwordsRequest: public Dictionary::WordSearchRequest
{
friend class StardictHeadwordsRequestRunnable;
wstring word;
StardictDictionary & dict;
QAtomicInt isCancelled;
QSemaphore hasExited;
public:
StardictHeadwordsRequest( wstring const & word_,
StardictDictionary & dict_ ):
word( word_ ), dict( dict_ )
{
QThreadPool::globalInstance()->start(
new StardictHeadwordsRequestRunnable( *this, hasExited ) );
}
void run(); // Run from another thread by StardictHeadwordsRequestRunnable
virtual void cancel()
{
isCancelled.ref();
}
~StardictHeadwordsRequest()
{
isCancelled.ref();
hasExited.acquire();
}
};
void StardictHeadwordsRequestRunnable::run()
{
r.run();
}
void StardictHeadwordsRequest::run()
{
if ( isCancelled )
{
finish();
return;
}
vector< WordArticleLink > chain = dict.findArticles( word );
wstring caseFolded = Folding::applySimpleCaseOnly( word );
for( unsigned x = 0; x < chain.size(); ++x )
{
if ( isCancelled )
{
finish();
return;
}
string headword, articleText;
loadArticle( chain[ x ].articleOffset,
headword, articleText );
dict.loadArticle( chain[ x ].articleOffset,
headword, articleText );
wstring headwordDecoded = Utf8::decode( headword );
@ -434,24 +519,103 @@ sptr< Dictionary::WordSearchRequest > StardictDictionary::findHeadwordsForSynony
{
// The headword seems to differ from the input word, which makes the
// input word its synonym.
result->getMatches().push_back( headwordDecoded );
Mutex::Lock _( dataMutex );
matches.push_back( headwordDecoded );
}
}
return result;
finish();
}
sptr< Dictionary::DataRequest > StardictDictionary::getArticle( wstring const & word,
vector< wstring > const & alts )
sptr< Dictionary::WordSearchRequest >
StardictDictionary::findHeadwordsForSynonym( wstring const & word )
throw( std::exception )
{
vector< WordArticleLink > chain = findArticles( word );
return new StardictHeadwordsRequest( word, *this );
}
/// StardictDictionary::getArticle()
class StardictArticleRequest;
class StardictArticleRequestRunnable: public QRunnable
{
StardictArticleRequest & r;
QSemaphore & hasExited;
public:
StardictArticleRequestRunnable( StardictArticleRequest & r_,
QSemaphore & hasExited_ ): r( r_ ),
hasExited( hasExited_ )
{}
~StardictArticleRequestRunnable()
{
hasExited.release();
}
virtual void run();
};
class StardictArticleRequest: public Dictionary::DataRequest
{
friend class StardictArticleRequestRunnable;
wstring word;
vector< wstring > alts;
StardictDictionary & dict;
QAtomicInt isCancelled;
QSemaphore hasExited;
public:
StardictArticleRequest( wstring const & word_,
vector< wstring > const & alts_,
StardictDictionary & dict_ ):
word( word_ ), alts( alts_ ), dict( dict_ )
{
QThreadPool::globalInstance()->start(
new StardictArticleRequestRunnable( *this, hasExited ) );
}
void run(); // Run from another thread by StardictArticleRequestRunnable
virtual void cancel()
{
isCancelled.ref();
}
~StardictArticleRequest()
{
isCancelled.ref();
hasExited.acquire();
}
};
void StardictArticleRequestRunnable::run()
{
r.run();
}
void StardictArticleRequest::run()
{
if ( isCancelled )
{
finish();
return;
}
vector< WordArticleLink > chain = dict.findArticles( word );
for( unsigned x = 0; x < alts.size(); ++x )
{
/// Make an additional query for each alt
vector< WordArticleLink > altChain = findArticles( alts[ x ] );
vector< WordArticleLink > altChain = dict.findArticles( alts[ x ] );
chain.insert( chain.end(), altChain.begin(), altChain.end() );
}
@ -466,6 +630,12 @@ sptr< Dictionary::DataRequest > StardictDictionary::getArticle( wstring const &
for( unsigned x = 0; x < chain.size(); ++x )
{
if ( isCancelled )
{
finish();
return;
}
if ( articlesIncluded.find( chain[ x ].articleOffset ) != articlesIncluded.end() )
continue; // We already have this article in the body.
@ -473,7 +643,7 @@ sptr< Dictionary::DataRequest > StardictDictionary::getArticle( wstring const &
string headword, articleText;
loadArticle( chain[ x ].articleOffset, headword, articleText );
dict.loadArticle( chain[ x ].articleOffset, headword, articleText );
// Ok. Now, does it go to main articles, or to alternate ones? We list
// main ones first, and alternates after.
@ -495,7 +665,11 @@ sptr< Dictionary::DataRequest > StardictDictionary::getArticle( wstring const &
}
if ( mainArticles.empty() && alternateArticles.empty() )
return new Dictionary::DataRequestInstant( false ); // No such word
{
// No such word
finish();
return;
}
string result;
@ -524,16 +698,23 @@ sptr< Dictionary::DataRequest > StardictDictionary::getArticle( wstring const &
result += cleaner;
}
Dictionary::DataRequestInstant * ret =
new Dictionary::DataRequestInstant( true );
Mutex::Lock _( dataMutex );
ret->getData().resize( result.size() );
data.resize( result.size() );
memcpy( &(ret->getData().front()), result.data(), result.size() );
memcpy( &data.front(), result.data(), result.size() );
return ret;
hasAnyData = true;
finish();
}
sptr< Dictionary::DataRequest > StardictDictionary::getArticle( wstring const & word,
vector< wstring > const & alts )
throw( std::exception )
{
return new StardictArticleRequest( word, alts, *this );
}
static char const * beginsWith( char const * substr, char const * str )