void FindResultsTab::OnRepeatOutput(wxCommandEvent& e) { wxUnusedVar(e); SearchData* searchData = GetSearchData(); searchData->UseNewTab(false); SearchThreadST::Get()->PerformSearch(*searchData); }
void FindInFilesDialog::DoSearchReplace() { SearchData data = DoGetSearchData(); data.SetOwner(clMainFrame::Get()->GetOutputPane()->GetReplaceResultsTab()); DoSaveOpenFiles(); SearchThreadST::Get()->PerformSearch(data); Close(); }
void FindResultsTab::OnSearchMatch(wxCommandEvent& e) { SearchResultList* res = (SearchResultList*)e.GetClientData(); if(!res) return; int m = m_book ? m_book->GetPageIndex(m_recv) : 0; if(m == wxNOT_FOUND) { wxDELETE(res); return; } MatchInfo& matchInfo = GetMatchInfo(m); for(SearchResultList::iterator iter = res->begin(); iter != res->end(); iter++) { if(matchInfo.empty() || matchInfo.rbegin()->second.GetFileName() != iter->GetFileName()) { if(!matchInfo.empty()) { AppendText("\n"); } wxFileName fn(iter->GetFileName()); fn.MakeRelativeTo(); AppendText(fn.GetFullPath() + wxT("\n")); } int lineno = m_recv->GetLineCount() - 1; matchInfo.insert(std::make_pair(lineno, *iter)); wxString text = iter->GetPattern(); int delta = -text.Length(); text.Trim(false); delta += text.Length(); text.Trim(); wxString linenum; if(iter->GetMatchState() == CppWordScanner::STATE_CPP_COMMENT || iter->GetMatchState() == CppWordScanner::STATE_C_COMMENT) linenum = wxString::Format(wxT(" %5u //"), iter->GetLineNumber()); else linenum = wxString::Format(wxT(" %5u "), iter->GetLineNumber()); SearchData* d = GetSearchData(m_recv); // Print the scope name if(d->GetDisplayScope()) { TagEntryPtr tag = TagsManagerST::Get()->FunctionFromFileLine(iter->GetFileName(), iter->GetLineNumber()); wxString scopeName(wxT("global")); if(tag) { scopeName = tag->GetPath(); } linenum << wxT("[ ") << scopeName << wxT(" ] "); iter->SetScope(scopeName); } delta += linenum.Length(); AppendText(linenum + text + wxT("\n")); m_recv->IndicatorFillRange(m_sci->PositionFromLine(lineno) + iter->GetColumn() + delta, iter->GetLen()); } wxDELETE(res); }
void FindInFilesDialog::DoSearch() { SearchData data = DoGetSearchData(); data.SetOwner(clMainFrame::Get()->GetOutputPane()->GetFindResultsTab()); // check to see if we require to save the files DoSaveOpenFiles(); SearchThreadST::Get()->PerformSearch(data); Close(); }
void ReplaceInFilesPanel::OnSearchStart(wxCommandEvent& e) { e.Skip(); // set the "Replace With" field with the user value SearchData* data = (SearchData*)e.GetClientData(); m_replaceWith->ChangeValue(data->GetReplaceWith()); // FindResultsTab::OnSearchStart(e); }
void SearchThread::ProcessRequest(ThreadRequest *req) { wxStopWatch sw; m_summary = SearchSummary(); DoSearchFiles(req); m_summary.SetElapsedTime(sw.Time()); SearchData *sd = (SearchData*)req; // Send search end event SendEvent(wxEVT_SEARCH_THREAD_SEARCHEND, sd->GetOwner()); }
void SearchOperation::doSearch( SearchData& searchData, qint64 initialLine ) { const qint64 nbSourceLines = sourceLogData_->getNbLine(); int maxLength = 0; int nbMatches = searchData.getNbMatches(); SearchResultArray currentList = SearchResultArray(); // Ensure no re-alloc will be done currentList.reserve( nbLinesInChunk ); LOG(logDEBUG) << "Searching from line " << initialLine << " to " << nbSourceLines; if (initialLine < startLine_) { initialLine = startLine_; } const qint64 endLine = qMin(nbSourceLines, endLine_); for ( qint64 i = initialLine; i < endLine; i += nbLinesInChunk ) { if ( *interruptRequested_ ) break; const int percentage = ( i - initialLine ) * 100 / ( endLine - initialLine ); emit searchProgressed( nbMatches, percentage ); const QStringList lines = sourceLogData_->getLines( i, qMin( nbLinesInChunk, (int) ( endLine - i ) ) ); LOG(logDEBUG) << "Chunk starting at " << i << ", " << lines.size() << " lines read."; int j = 0; for ( ; j < lines.size(); j++ ) { if ( regexp_.match( lines[j] ).hasMatch() ) { // FIXME: increase perf by removing temporary const int length = sourceLogData_->getExpandedLineString(i+j).length(); if ( length > maxLength ) maxLength = length; currentList.push_back( MatchingLine( i+j ) ); nbMatches++; } } // After each block, copy the data to shared data // and update the client searchData.addAll( maxLength, currentList, i+j ); currentList.clear(); } emit searchProgressed( nbMatches, 100 ); }
// Called in the worker thread's context void FullSearchOperation::start( SearchData& searchData ) { // Clear the shared data searchData.clear(); doSearch( searchData, 0 ); }
void FindResultsTab::OnSearchMatch(wxCommandEvent& e) { SearchResultList* res = (SearchResultList*)e.GetClientData(); if(!res) return; SearchResultList::iterator iter = res->begin(); for(; iter != res->end(); ++iter) { if(m_matchInfo.empty() || m_matchInfo.rbegin()->second.GetFileName() != iter->GetFileName()) { if(!m_matchInfo.empty()) { AppendText("\n"); } wxFileName fn(iter->GetFileName()); fn.MakeRelativeTo(); AppendText(fn.GetFullPath() + wxT("\n")); } int lineno = m_sci->GetLineCount() - 1; m_matchInfo.insert(std::make_pair(lineno, *iter)); wxString text = iter->GetPattern(); // int delta = -text.Length(); // text.Trim(false); // delta += text.Length(); // text.Trim(); wxString linenum = wxString::Format(wxT(" %5u: "), iter->GetLineNumber()); SearchData* d = GetSearchData(); // Print the scope name if(d->GetDisplayScope()) { TagEntryPtr tag = TagsManagerST::Get()->FunctionFromFileLine(iter->GetFileName(), iter->GetLineNumber()); wxString scopeName(wxT("global")); if(tag) { scopeName = tag->GetPath(); } linenum << wxT("[ ") << scopeName << wxT(" ] "); iter->SetScope(scopeName); } AppendText(linenum + text + wxT("\n")); int indicatorStartPos = m_sci->PositionFromLine(lineno) + iter->GetColumn() + linenum.Length(); int indicatorLen = iter->GetLen(); m_indicators.push_back(indicatorStartPos); m_sci->IndicatorFillRange(indicatorStartPos, indicatorLen); } wxDELETE(res); }
void FindResultsTab::OnRepeatOutput(wxCommandEvent& e) { wxUnusedVar(e); if(m_book) { int sel = m_book->GetSelection(); if(sel != wxNOT_FOUND) { // get the search data used to generate the output on the selected tab wxWindow* tab = m_book->GetPage(sel); if(tab) { SearchData* searchData = (SearchData*)tab->GetClientData(); searchData->UseNewTab(false); SearchThreadST::Get()->PerformSearch(*searchData); } } } }
void FindResultsTab::OnSearchStart(wxCommandEvent& e) { m_searchInProgress = true; Clear(); SetStyles(m_sci); SearchData* data = (SearchData*)e.GetClientData(); if(data) { m_searchData = *data; wxString message; message << _("====== Searching for: '") << data->GetFindString() << _("'; Match case: ") << (data->IsMatchCase() ? _("true") : _("false")) << _(" ; Match whole word: ") << (data->IsMatchWholeWord() ? _("true") : _("false")) << _(" ; Regular expression: ") << (data->IsRegularExpression() ? _("true") : _("false")) << wxT(" ======\n"); AppendText(message); } wxDELETE(data); }
void FindResultsTab::OnSearchStart(wxCommandEvent& e) { m_searchInProgress = true; SearchData* data = (SearchData*)e.GetClientData(); wxString label = data ? data->GetFindString() : wxT(""); if(e.GetInt() != 0 || m_sci == NULL) { if(m_book) { clWindowUpdateLocker locker(this); MySTC* sci = new MySTC(m_book); SetStyles(sci); sci->Connect(wxEVT_STC_STYLENEEDED, wxStyledTextEventHandler(FindResultsTab::OnStyleNeeded), NULL, this); m_book->AddPage(sci, label, true); #ifdef __WXMAC__ m_book->GetSizer()->Layout(); #endif size_t where = m_book->GetPageCount() - 1; // keep the search data used for this tab wxWindow* tab = m_book->GetPage(where); if(tab) { tab->SetClientData(data); } m_matchInfo.push_back(MatchInfo()); m_sci = sci; } } else if(m_book) { // using current tab, update the tab title and the search data int where = m_book->GetPageIndex(m_sci); if(where != wxNOT_FOUND) { m_book->SetPageText(where, label); // delete the old search data wxWindow* tab = m_book->GetPage(where); SearchData* oldData = (SearchData*)tab->GetClientData(); if(oldData) { delete oldData; } // set the new search data tab->SetClientData(data); } } // This is needed in >=wxGTK-2.9, otherwise the 'Search' pane doesn't fully expand SendSizeEvent(wxSEND_EVENT_POST); m_recv = m_sci; Clear(); if(data) { m_searchData = *data; wxString message; message << _("====== Searching for: '") << data->GetFindString() << _("'; Match case: ") << (data->IsMatchCase() ? _("true") : _("false")) << _(" ; Match whole word: ") << (data->IsMatchWholeWord() ? _("true") : _("false")) << _(" ; Regular expression: ") << (data->IsRegularExpression() ? _("true") : _("false")) << wxT(" ======\n"); AppendText(message); } }
SearchData TaskPanel::DoGetSearchData() { SearchData data; data.SetDisplayScope(true); data.SetRegularExpression(true); data.SetMatchCase(false); data.SetMatchWholeWord(false); data.SetEncoding(m_choiceEncoding->GetStringSelection()); data.SetOwner(this); wxString sfind; // Load all info from disk TasksPanelData d; EditorConfigST::Get()->ReadObject(wxT("TasksPanelData"), &d); wxStringMap_t::const_iterator iter = d.GetTasks().begin(); for(; iter != d.GetTasks().end(); iter++) { wxString name = iter->first; wxString regex = iter->second; bool enabled = (d.GetEnabledItems().Index(iter->first) != wxNOT_FOUND); regex.Trim().Trim(false); wxRegEx re(regex); if(enabled && !regex.IsEmpty() && re.IsValid()) sfind << wxT("(") << regex << wxT(")|"); } if(sfind.empty() == false) sfind.RemoveLast(); data.SetFindString(sfind); wxString rootDir = clWorkspaceManager::Get().GetWorkspace()->GetFileName().GetPath(); wxArrayString rootDirs; rootDirs.push_back(rootDir); data.SetRootDirs(rootDirs); data.SetExtensions(wxT("*.*")); return data; }
// Called in the worker thread's context void UpdateSearchOperation::start( SearchData& searchData ) { qint64 initial_line = initialPosition_; if ( initial_line >= 1 ) { // We need to re-search the last line because it might have // been updated (if it was not LF-terminated) --initial_line; // In case the last line matched, we don't want it to match twice. searchData.deleteMatch( initial_line ); } doSearch( searchData, initial_line ); }
void SearchThread::DoSearchFiles(ThreadRequest *req) { SearchData *data = static_cast<SearchData*>(req); // Get all files if ( data->GetRootDirs().IsEmpty() ) return; if ( data->GetFindString().IsEmpty() ) return; StopSearch(false); wxArrayString fileList; GetFiles(data, fileList); wxStopWatch sw; // Send startup message to main thread if ( m_notifiedWindow || data->GetOwner() ) { wxCommandEvent event(wxEVT_SEARCH_THREAD_SEARCHSTARTED, GetId()); event.SetClientData(new SearchData(*data)); //set the rquested output tab event.SetInt(data->UseNewTab() ? 1 : 0); if (data->GetOwner()) { ::wxPostEvent(data->GetOwner(), event); } else { // since we are in if ( m_notifiedWindow || data->GetOwner() ) block... ::wxPostEvent(m_notifiedWindow, event); } } for (size_t i=0; i<fileList.Count(); i++) { m_summary.SetNumFileScanned((int)i+1); // give user chance to cancel the search ... if ( TestStopSearch() ) { // Send cancel event SendEvent(wxEVT_SEARCH_THREAD_SEARCHCANCELED, data->GetOwner()); StopSearch(false); break; } DoSearchFile(fileList.Item(i), data); } }
/*code implements Greedy search*/ bool Design::greedy(int dest){ #if DEBUG==3 fprintf(debugfile_,"GREEDY"); fprintf(debugfile_,"dest: %d\n",dest); fflush(debugfile_); #endif MinHeap<SearchData> openlist; CloseStructure* closelist=new CloseStructure[map_.numvert()]; bool rc=false; Node<EdgeInfo>* currconnect; int nn; SearchData curr; SearchData tmp; curr.set(whichbox_,-1,0); //start at current node, parent is -1. //uniform cost so cost incurred so far is 0 bool done=false; bool found=false; float biggestcost=0; int numclosed=0; do{ #if DEBUG==3 fprintf(debugfile_,"curr.nodenum: %d\n",curr.nodenum()); fflush(debugfile_); #endif LList<EdgeInfo>& edgelist=map_.edges(curr.nodenum()); //get the conections to curr while(currconnect=edgelist.curr()){ //for each node connected to curr nn=currconnect->data().to(); if(!closelist[nn].closed_){ //if its not in the closed list #if DEBUG==3 fprintf(debugfile_,"add to openlist: %d\n",nn); fflush(debugfile_); #endif tmp.set(nn,curr.nodenum(),timeleft(currconnect,dest)); openlist.insert(tmp); } edgelist.gonext(); }//while closelist[curr.nodenum()].closed_=true; //add it to the close list closelist[curr.nodenum()].nodeinf_=curr; numclosed++; if(!openlist.isempty()){ //if there are still nodes to consider curr=openlist.remove();//remove lowest cost item from list #if DEBUG == 3 fprintf(debugfile_,"removed from openlist: %d\n",curr.nodenum()); fflush(debugfile_); #endif while(!openlist.isempty() && closelist[curr.nodenum()].closed_){ //if already encountered curr=openlist.remove();//remove lowest cost item from list //consider the next node } if(closelist[curr.nodenum()].closed_){ //only way to reach this part of code is if the open list is empty and //we have not found the next node to examine done=true; } else{ if(curr.nodenum()==dest){ found=true; //found a node to the destination... question is, is it the best. biggestcost=curr.cost(); } if(found && curr.cost() > biggestcost){ done=true; } }//else still have node }//if list is not empty else{ done=true; }//open list is empty }while(!done); if(found){ rc=true; setPath(closelist,dest); delete [] closelist; } return rc; }
bool operator<(const SearchData& left, const SearchData& right){ bool rc=false; if(left.cost() < right.cost()) rc=true; return rc; }
SearchData FindInFilesDialog::DoGetSearchData() { SearchData data; wxString findStr(m_data.GetFindString()); if(!m_findString->GetValue().IsEmpty()) { findStr = m_findString->GetValue(); } data.SetFindString(findStr); data.SetReplaceWith(m_replaceString->GetValue()); m_data.SetFlags(GetSearchFlags()); size_t flags = m_data.GetFlags(); // If the 'Skip comments' is ON, remove the // 'colour comments' flag if(flags & wxFRD_SKIP_COMMENTS) { flags &= ~wxFRD_COLOUR_COMMENTS; } data.SetMatchCase((flags & wxFRD_MATCHCASE) != 0); data.SetMatchWholeWord((flags & wxFRD_MATCHWHOLEWORD) != 0); data.SetRegularExpression((flags & wxFRD_REGULAREXPRESSION) != 0); data.SetDisplayScope((flags & wxFRD_DISPLAYSCOPE) != 0); data.SetEncoding(m_choiceEncoding->GetStringSelection()); data.SetSkipComments(flags & wxFRD_SKIP_COMMENTS); data.SetSkipStrings(flags & wxFRD_SKIP_STRINGS); data.SetColourComments(flags & wxFRD_COLOUR_COMMENTS); data.SetEnablePipeSupport(flags & wxFRD_ENABLE_PIPE_SUPPORT); wxArrayString searchWhere = m_listPaths->GetStrings(); wxArrayString files; wxArrayString rootDirs; for(size_t i = 0; i < searchWhere.GetCount(); ++i) { const wxString& rootDir = searchWhere.Item(i); // Check both translations and otherwise: the history may contain either if((rootDir == wxGetTranslation(SEARCH_IN_WORKSPACE)) || (rootDir == SEARCH_IN_WORKSPACE)) { if(!clWorkspaceManager::Get().IsWorkspaceOpened()) continue; clWorkspaceManager::Get().GetWorkspace()->GetWorkspaceFiles(files); } else if((rootDir == wxGetTranslation(SEARCH_IN_PROJECT)) || (rootDir == SEARCH_IN_PROJECT)) { if(!clWorkspaceManager::Get().IsWorkspaceOpened()) continue; if(clWorkspaceManager::Get().GetWorkspace()->IsProjectSupported()) { // get the active project files clWorkspaceManager::Get().GetWorkspace()->GetProjectFiles("", files); } else { // search the entire workspace clWorkspaceManager::Get().GetWorkspace()->GetWorkspaceFiles(files); } } else if((rootDir == wxGetTranslation(SEARCH_IN_CURR_FILE_PROJECT)) || (rootDir == SEARCH_IN_CURR_FILE_PROJECT)) { if(!clWorkspaceManager::Get().IsWorkspaceOpened()) continue; IEditor* editor = clGetManager()->GetActiveEditor(); if(!editor) continue; if(clWorkspaceManager::Get().GetWorkspace()->IsProjectSupported()) { wxString projectName = clWorkspaceManager::Get().GetWorkspace()->GetProjectFromFile(editor->GetFileName()); clWorkspaceManager::Get().GetWorkspace()->GetProjectFiles(projectName, files); } else { // search the entire workspace clWorkspaceManager::Get().GetWorkspace()->GetWorkspaceFiles(files); } } else if((rootDir == wxGetTranslation(SEARCH_IN_CURRENT_FILE)) || (rootDir == SEARCH_IN_CURRENT_FILE)) { LEditor* editor = clMainFrame::Get()->GetMainBook()->GetActiveEditor(); if(editor) { files.Add(editor->GetFileName().GetFullPath()); } } else if((rootDir == wxGetTranslation(SEARCH_IN_OPEN_FILES)) || (rootDir == SEARCH_IN_OPEN_FILES)) { std::vector<LEditor*> editors; clMainFrame::Get()->GetMainBook()->GetAllEditors(editors, MainBook::kGetAll_IncludeDetached); for(size_t n = 0; n < editors.size(); ++n) { LEditor* editor = dynamic_cast<LEditor*>(*(editors.begin() + n)); if(editor) { files.Add(editor->GetFileName().GetFullPath()); } } } else if(wxFileName::DirExists(searchWhere.Item(i))) { rootDirs.Add(searchWhere.Item(i)); } } // Remove duplicates wxStringSet_t filesSet; wxArrayString uniqueFiles; std::for_each(files.begin(), files.end(), [&](const wxString& file) { if(filesSet.count(file) == 0) { filesSet.insert(file); uniqueFiles.Add(file); } }); files.swap(uniqueFiles); data.SetFiles(files); // list of files data.SetRootDirs(rootDirs); // folders data.UseNewTab(false); data.SetExtensions(m_fileTypes->GetValue()); return data; }
SearchData TaskPanel::DoGetSearchData() { SearchData data; data.SetDisplayScope(true); data.SetRegularExpression(true); data.SetMatchCase(false); data.SetMatchWholeWord(false); data.SetEncoding(m_choiceEncoding->GetStringSelection()); data.SetOwner(this); wxString sfind; // Load all info from disk TasksPanelData d; EditorConfigST::Get()->ReadObject(wxT("TasksPanelData"), &d); std::map<wxString, wxString>::const_iterator iter = d.GetTasks().begin(); for(; iter != d.GetTasks().end(); iter++) { wxString name = iter->first; wxString regex = iter->second; bool enabled = (d.GetEnabledItems().Index(iter->first) != wxNOT_FOUND); regex.Trim().Trim(false); wxRegEx re(regex); if(enabled && !regex.IsEmpty() && re.IsValid()) sfind << wxT("(") << regex << wxT(")|"); } if(sfind.empty() == false) sfind.RemoveLast(); data.SetFindString(sfind); wxString rootDir = m_scope->GetStringSelection(); wxArrayString rootDirs; rootDirs.push_back(rootDir); data.SetRootDirs(rootDirs); wxArrayString files; if(rootDir == wxGetTranslation(SEARCH_IN_WORKSPACE)) { ManagerST::Get()->GetWorkspaceFiles(files); } else if(rootDir == wxGetTranslation(SEARCH_IN_PROJECT)) { ManagerST::Get()->GetActiveProjectFiles(files); } else if(rootDir == wxGetTranslation(SEARCH_IN_CURR_FILE_PROJECT)) { ManagerST::Get()->GetActiveFileProjectFiles(files); } data.SetFiles(files); data.SetExtensions(wxT("*.*")); return data; }
SearchData FindInFilesDialog::DoGetSearchData() { SearchData data; wxString findStr(m_data.GetFindString()); if(m_findString->GetValue().IsEmpty() == false) { findStr = m_findString->GetValue(); } data.SetFindString(findStr); m_data.SetFlags(GetSearchFlags()); size_t flags = m_data.GetFlags(); // If the 'Skip comments' is ON, remove the // 'colour comments' flag if(flags & wxFRD_SKIP_COMMENTS) { flags &= ~wxFRD_COLOUR_COMMENTS; } data.SetMatchCase((flags & wxFRD_MATCHCASE) != 0); data.SetMatchWholeWord((flags & wxFRD_MATCHWHOLEWORD) != 0); data.SetRegularExpression((flags & wxFRD_REGULAREXPRESSION) != 0); data.SetDisplayScope((flags & wxFRD_DISPLAYSCOPE) != 0); data.SetEncoding(m_choiceEncoding->GetStringSelection()); data.SetSkipComments(flags & wxFRD_SKIP_COMMENTS); data.SetSkipStrings(flags & wxFRD_SKIP_STRINGS); data.SetColourComments(flags & wxFRD_COLOUR_COMMENTS); wxArrayString rootDirs; for(size_t i = 0; i < m_listPaths->GetCount(); ++i) { rootDirs.push_back(m_listPaths->GetString(i)); } if(rootDirs.IsEmpty()) { wxString dir = m_dirPicker->GetPath(); if(dir.IsEmpty() == false) { rootDirs.push_back(dir); } } data.SetRootDirs(rootDirs); wxArrayString files; for(size_t i = 0; i < rootDirs.GetCount(); ++i) { const wxString& rootDir = rootDirs.Item(i); // Check both translations and otherwise: the history may contain either if((rootDir == wxGetTranslation(SEARCH_IN_WORKSPACE)) || (rootDir == SEARCH_IN_WORKSPACE)) { ManagerST::Get()->GetWorkspaceFiles(files); } else if((rootDir == wxGetTranslation(SEARCH_IN_PROJECT)) || (rootDir == SEARCH_IN_PROJECT)) { ManagerST::Get()->GetActiveProjectFiles(files); } else if((rootDir == wxGetTranslation(SEARCH_IN_CURR_FILE_PROJECT)) || (rootDir == SEARCH_IN_CURR_FILE_PROJECT)) { ManagerST::Get()->GetActiveFileProjectFiles(files); } else if((rootDir == wxGetTranslation(SEARCH_IN_CURRENT_FILE)) || (rootDir == SEARCH_IN_CURRENT_FILE)) { LEditor* editor = clMainFrame::Get()->GetMainBook()->GetActiveEditor(); if(editor) { files.Add(editor->GetFileName().GetFullPath()); } } else if((rootDir == wxGetTranslation(SEARCH_IN_OPEN_FILES)) || (rootDir == SEARCH_IN_OPEN_FILES)) { std::vector<LEditor*> editors; clMainFrame::Get()->GetMainBook()->GetAllEditors(editors, MainBook::kGetAll_IncludeDetached); for(size_t n = 0; n < editors.size(); ++n) { LEditor* editor = dynamic_cast<LEditor*>(*(editors.begin() + n)); if(editor) { files.Add(editor->GetFileName().GetFullPath()); } } } } data.SetFiles(files); data.UseNewTab(m_checkBoxSeparateTab->IsChecked()); data.SetExtensions(m_fileTypes->GetValue()); return data; }
void SearchOperation::doSearch( SearchData& searchData, LineNumber initialLine ) { const auto& config = Persistable::get<Configuration>(); const auto nbSourceLines = sourceLogData_->getNbLine(); LineLength maxLength = 0_length; LinesCount nbMatches = searchData.getNbMatches(); const auto nbLinesInChunk = LinesCount( config.searchReadBufferSizeLines() ); LOG( logDEBUG ) << "Searching from line " << initialLine << " to " << nbSourceLines; if ( initialLine < startLine_ ) { initialLine = startLine_; } const auto endLine = qMin( LineNumber( nbSourceLines.get() ), endLine_ ); using namespace std::chrono; high_resolution_clock::time_point t1 = high_resolution_clock::now(); QSemaphore searchCompleted; QSemaphore blocksDone; using SearchBlockQueue = moodycamel::BlockingConcurrentQueue<SearchBlockData>; using ProcessMatchQueue = moodycamel::BlockingConcurrentQueue<PartialSearchResults>; SearchBlockQueue searchBlockQueue; ProcessMatchQueue processMatchQueue; std::vector<QFuture<void>> matchers; const auto matchingThreadsCount = [&config]() { if ( !config.useParallelSearch() ) { return 1; } return qMax( 1, config.searchThreadPoolSize() == 0 ? QThread::idealThreadCount() - 1 : static_cast<int>( config.searchThreadPoolSize() ) ); }(); LOG( logINFO ) << "Using " << matchingThreadsCount << " matching threads"; auto localThreadPool = std::make_unique<QThreadPool>(); localThreadPool->setMaxThreadCount( matchingThreadsCount + 2 ); const auto makeMatcher = [this, &searchBlockQueue, &processMatchQueue, pool = localThreadPool.get()]( size_t index ) { // copy and optimize regex for each thread auto regexp = QRegularExpression{ regexp_.pattern(), regexp_.patternOptions() }; regexp.optimize(); return QtConcurrent::run( pool, [regexp, index, &searchBlockQueue, &processMatchQueue]() { auto cToken = moodycamel::ConsumerToken{ searchBlockQueue }; auto pToken = moodycamel::ProducerToken{ processMatchQueue }; for ( ;; ) { SearchBlockData blockData; searchBlockQueue.wait_dequeue( cToken, blockData ); LOG( logDEBUG ) << "Searcher " << index << " " << blockData.chunkStart; const auto lastBlock = blockData.lines.empty(); if ( !lastBlock ) { blockData.results = filterLines( regexp, blockData.lines, blockData.chunkStart ); } LOG( logDEBUG ) << "Searcher " << index << " sending matches " << blockData.results.matchingLines.size(); processMatchQueue.enqueue( pToken, std::move( blockData.results ) ); if ( lastBlock ) { LOG( logDEBUG ) << "Searcher " << index << " last block"; return; } } } ); }; for ( int i = 0; i < matchingThreadsCount; ++i ) { matchers.emplace_back( makeMatcher( i ) ); } auto processMatches = QtConcurrent::run( localThreadPool.get(), [&]() { auto cToken = moodycamel::ConsumerToken{ processMatchQueue }; size_t matchersDone = 0; int reportedPercentage = 0; auto reportedMatches = nbMatches; LinesCount totalProcessedLines = 0_lcount; const auto totalLines = endLine - initialLine; for ( ;; ) { PartialSearchResults matchResults; processMatchQueue.wait_dequeue( cToken, matchResults ); LOG( logDEBUG ) << "Combining match results from " << matchResults.chunkStart; if ( matchResults.processedLines.get() ) { maxLength = qMax( maxLength, matchResults.maxLength ); nbMatches += LinesCount( static_cast<LinesCount::UnderlyingType>( matchResults.matchingLines.size() ) ); const auto processedLines = LinesCount{ matchResults.chunkStart.get() + matchResults.processedLines.get() }; totalProcessedLines += matchResults.processedLines; // After each block, copy the data to shared data // and update the client searchData.addAll( maxLength, matchResults.matchingLines, processedLines ); LOG( logDEBUG ) << "done Searching chunk starting at " << matchResults.chunkStart << ", " << matchResults.processedLines << " lines read."; blocksDone.release( matchResults.processedLines.get() ); } else { matchersDone++; } const int percentage = static_cast<int>( std::floor( 100.f * ( totalProcessedLines ).get() / totalLines.get() ) ); if ( percentage > reportedPercentage || nbMatches > reportedMatches ) { emit searchProgressed( nbMatches, std::min( 99, percentage ), initialLine ); reportedPercentage = percentage; reportedMatches = nbMatches; } if ( matchersDone == matchers.size() ) { searchCompleted.release(); return; } } } ); auto pToken = moodycamel::ProducerToken{ searchBlockQueue }; blocksDone.release( nbLinesInChunk.get() * ( static_cast<uint32_t>( matchers.size() ) + 1 ) ); for ( auto chunkStart = initialLine; chunkStart < endLine; chunkStart = chunkStart + nbLinesInChunk ) { if ( *interruptRequested_ ) break; LOG( logDEBUG ) << "Reading chunk starting at " << chunkStart; const auto linesInChunk = LinesCount( qMin( nbLinesInChunk.get(), ( endLine - chunkStart ).get() ) ); auto lines = sourceLogData_->getLines( chunkStart, linesInChunk ); LOG( logDEBUG ) << "Sending chunk starting at " << chunkStart << ", " << lines.size() << " lines read."; blocksDone.acquire( static_cast<uint32_t>( lines.size() ) ); searchBlockQueue.enqueue( pToken, { chunkStart, std::move( lines ), PartialSearchResults{} } ); LOG( logDEBUG ) << "Sent chunk starting at " << chunkStart << ", " << lines.size() << " lines read."; } for ( size_t i = 0; i < matchers.size(); ++i ) { searchBlockQueue.enqueue( pToken, { endLine, std::vector<QString>{}, PartialSearchResults{} } ); } searchCompleted.acquire(); high_resolution_clock::time_point t2 = high_resolution_clock::now(); auto duration = duration_cast<microseconds>( t2 - t1 ).count(); LOG( logINFO ) << "Searching done, took " << duration / 1000.f << " ms"; LOG( logINFO ) << "Searching perf " << static_cast<uint32_t>( std::floor( 1000 * 1000.f * ( endLine - initialLine ).get() / duration ) ) << " lines/s"; emit searchProgressed( nbMatches, 100, initialLine ); }