diff --git a/src/bgl.cc b/src/bgl.cc index b56dc1eb..fdb73ce7 100644 --- a/src/bgl.cc +++ b/src/bgl.cc @@ -15,6 +15,10 @@ #include #include +#include +#include +#include + 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 1 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 1 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 ) + "" + in.substr( x + 1, in.size() - x - 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 - if ( !isdigit( in[ x ] ) ) - break; + return in.substr( 0, x ) + "" + in.substr( x + 1, in.size() - x - 2 ) + ""; } - - 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 = """""""""""" - """""""""""" - "" - ""; - - for( i = mainArticles.begin(); i != mainArticles.end(); ++i ) - { - result += "

"; - result += postfixToSuperscript( i->second.first ); - result += "

"; - result += i->second.second; - result += cleaner; - } - - for( i = alternateArticles.begin(); i != alternateArticles.end(); ++i ) - { - result += "

"; - result += postfixToSuperscript( i->second.first ); - result += "

"; - 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 = """""""""""" + """""""""""" + "" + ""; + + for( i = mainArticles.begin(); i != mainArticles.end(); ++i ) + { + result += "

"; + result += postfixToSuperscript( i->second.first ); + result += "

"; + result += i->second.second; + result += cleaner; + } + + for( i = alternateArticles.begin(); i != alternateArticles.end(); ++i ) + { + result += "

"; + result += postfixToSuperscript( i->second.first ); + result += "

"; + 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 1234; occurences with ሴ void BglDictionary::replaceCharsetEntities( string & text ) { diff --git a/src/dsl.cc b/src/dsl.cc index 58b32177..f183c62f 100644 --- a/src/dsl.cc +++ b/src/dsl.cc @@ -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 += ""; @@ -623,10 +708,10 @@ sptr< Dictionary::DataRequest > DslDictionary::getArticle( wstring const & word, expandTildes( articleBody, displayedHeadwords.front() ); articleText += "
"; - articleText += dslToHtml( articleBody ); + articleText += dict.dslToHtml( articleBody ); articleText += "
"; articleText += ""; - + // 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 ) diff --git a/src/stardict.cc b/src/stardict.cc index bc038e69..6884ca60 100644 --- a/src/stardict.cc +++ b/src/stardict.cc @@ -21,6 +21,9 @@ #include #include +#include +#include +#include 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 )