LCOV - code coverage report
Current view: top level - src - sortcache.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 365 393 92.9 %
Date: 2019-01-29 11:06:41 Functions: 13 14 92.9 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2011  Gerd Lorscheid
       3             :  * Copyright (C) 2011-2017  Fulvio Benini
       4             :  *
       5             :  * This file is part of Scid (Shane's Chess Information Database).
       6             :  *
       7             :  * Scid is free software: you can redistribute it and/or modify
       8             :  * it under the terms of the GNU General Public License as published by
       9             :  * the Free Software Foundation.
      10             :  *
      11             :  * Scid is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :  * GNU General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License
      17             :  * along with Scid.  If not, see <http://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : /** @file
      21             :  * Implements the SortCache class, which sorts the games of an @e Index.
      22             :  */
      23             : 
      24             : #include "sortcache.h"
      25             : #include "hfilter.h"
      26             : #include "index.h"
      27             : #include "misc.h"
      28             : #include <algorithm>
      29             : #include <climits>
      30             : #include <cstring>
      31             : #include <numeric>
      32             : 
      33             : #ifndef MULTITHREADING_OFF
      34             : #include <thread>
      35             : 
      36             : /**
      37             :  * Blocks the current thread until the thread @e th_ finishes its execution.
      38             :  */
      39       15133 : void SortCache::th_join() {
      40       15133 :         if (th_ != nullptr) {
      41          76 :                 static_cast<std::thread*>(th_)->join();
      42          76 :                 delete static_cast<std::thread*>(th_);
      43          76 :                 th_ = nullptr;
      44             :         }
      45       15133 : }
      46             : 
      47             : /**
      48             :  * Populate @e fullMap_ with the sorted ids of all the games contained into @e
      49             :  * index_. This function can run in a worker thread and can be interrupted.
      50             :  * It is necessary to invoke th_interrupt() or th_join() before modifying the
      51             :  * SortCache object, or the associated Index and NameBase objects.
      52             :  */
      53          76 : void SortCache::th_sort() {
      54          76 :         size_t nGames = this->nGames_;
      55          76 :         gamenumT* v = this->fullMap_;
      56          76 :         gamenumT* begin = v;
      57          76 :         gamenumT* end = v + nGames;
      58          76 :         auto comp = SortCache::CmpLess(this);
      59             : 
      60          76 :         std::iota(begin, end, 0);
      61             : 
      62             :         // An interruptible implementation of:
      63             :         // std::make_heap(v.begin(), v.end(), comp);
      64          76 :         ASSERT(nGames < size_t(INT_MAX / 2));
      65          76 :         const int lastNode = static_cast<int>(nGames) - 1;
      66          76 :         const auto lastRoot = (lastNode - 1) / 2;
      67       50251 :         for (auto node = lastRoot; node >= 0; --node) {
      68       50176 :                 if (this->th_interrupt_)
      69           2 :                         return;
      70             :                 // Sift down @e node
      71       50175 :                 for (auto toSift = node;;) {
      72      125330 :                         const auto leftChild = 2 * toSift + 1;
      73      125330 :                         const auto rightChild = 2 * toSift + 2;
      74      125330 :                         if (leftChild > lastNode)
      75       32664 :                                 break;
      76       92666 :                         int maxChild = (rightChild <= lastNode && comp(v[leftChild], v[rightChild]))
      77      145560 :                                            ? rightChild
      78       92666 :                                            : leftChild;
      79       92666 :                         if (!comp(v[toSift], v[maxChild]))
      80       17511 :                                 break;
      81       75155 :                         std::swap(v[toSift], v[maxChild]);
      82       75155 :                         toSift = maxChild;
      83       75155 :                 }
      84             :         }
      85          75 :         ASSERT(std::is_heap(begin, end, comp));
      86             : 
      87             :         // An interruptible implementation of:
      88             :         // std::sort_heap(v.begin(), v.end(), comp);
      89      100500 :         for (auto it = end; it != begin; --it) {
      90      100425 :                 if (this->th_interrupt_)
      91           0 :                         return;
      92      100425 :                 std::pop_heap(begin, it, comp);
      93             :         }
      94          75 :         ASSERT(std::is_sorted(begin, end, comp));
      95             : 
      96          75 :         this->valid_fullMap_ = true;
      97             : }
      98             : 
      99             : #else
     100             : void SortCache::th_join() {}
     101             : void SortCache::th_sort() {}
     102             : #endif
     103             : 
     104             : 
     105       15133 : SortCache::SortCache(const Index* idx, const NameBase* nbase)
     106             :     : nGames_(0), valid_fullMap_(false), th_interrupt_(false),
     107             :       partialHash_(false), fullMap_(NULL), th_(NULL), hash_(NULL), index_(idx),
     108       15133 :       nbase_(nbase), refCount_(0) {}
     109             : 
     110       30266 : SortCache::~SortCache() {
     111       15133 :         th_interrupt();
     112       15133 :         delete[] hash_;
     113       15133 :         delete[] fullMap_;
     114       15133 : }
     115             : 
     116       15133 : SortCache* SortCache::create(const Index* idx, const NameBase* nb,
     117             :                              const char* criteria) {
     118       15133 :         ASSERT(idx != NULL && nb != NULL && criteria != NULL);
     119             : 
     120             :         static const char fields[] = {
     121             :             SORTING_date,       SORTING_year,         SORTING_event,
     122             :             SORTING_site,       SORTING_round,        SORTING_white,
     123             :             SORTING_black,      SORTING_eco,          SORTING_result,
     124             :             SORTING_moveCount,  SORTING_avgElo,       SORTING_country,
     125             :             SORTING_deleted,    SORTING_eventdate,    SORTING_whiteelo,
     126             :             SORTING_blackelo,   SORTING_commentcount, SORTING_varcount,
     127             :             SORTING_nagcount,   SORTING_resultwin,    SORTING_resultdraw,
     128             :             SORTING_resultloss, SORTING_rating,       SORTING_number,
     129             :             SORTING_sentinel};
     130             :         static const char* fields_end = fields + sizeof(fields);
     131             : 
     132       15133 :         if (*criteria == '\0') // Invalid empty criteria.
     133           0 :                 return NULL;
     134             : 
     135       15133 :         SortCache* sc = new SortCache(idx, nb);
     136             : 
     137             :         // Parse the sorting criteria.
     138       15133 :         size_t i = 0;
     139       15361 :         for (const char *it = criteria; *it != 0; ++it) {
     140       15285 :                 bool valid = std::find(fields, fields_end, *it) != fields_end;
     141       15285 :                 sc->criteria_[i++] = *it++;
     142       15285 :                 bool reverse = (*it != '+');
     143       15285 :                 sc->criteria_[i++] = reverse ? 1 : 0;
     144       15285 :                 if (!valid                         // Unknown field
     145         732 :                     || (reverse && *it != '-')     // Invalid asc/desc param
     146         228 :                     || i >= sizeof(sc->criteria_)) // No space left for SORTING_sentinel
     147             :                 {
     148       15057 :                         delete sc;
     149       15057 :                         return NULL;
     150             :                 }
     151             :         }
     152          76 :         sc->criteria_[i] = SORTING_sentinel;
     153             : 
     154          76 :         sc->generateHashCache();
     155          76 :         sc->sortAsynchronously();
     156             : 
     157          76 :         return sc;
     158             : }
     159             : 
     160        1560 : size_t SortCache::select(size_t row_offset, size_t row_count,
     161             :                          const HFilter& filter, gamenumT* result) const {
     162        1560 :         ASSERT(filter != NULL && filter->size() <= nGames_);
     163        1560 :         ASSERT(result != NULL);
     164             : 
     165        1560 :         size_t maxResults = filter->size();
     166        1560 :         if (row_count == 0 || row_offset >= maxResults)
     167           0 :                 return 0;
     168             : 
     169        1560 :         size_t row_end = std::min(row_offset + row_count, maxResults);
     170             : 
     171        1560 :         if (!valid_fullMap_) {
     172         223 :                 gamenumT* v = new gamenumT[maxResults];
     173         223 :                 gamenumT* v_end = v + maxResults;
     174         223 :                 if (maxResults == nGames_) {
     175             :                         // std::iota(v, v_end, 0);
     176      298820 :                         for (gamenumT i = 0; i < maxResults; ++i) {
     177      298597 :                                 v[i] = i;
     178             :                         }
     179             :                 } else {
     180           0 :                         std::copy(filter->begin(), filter->end(), v);
     181             :                 }
     182             : 
     183         223 :                 size_t skip = 0;
     184         223 :                 if (row_offset > 1000) {
     185          31 :                         skip = row_offset;
     186          31 :                         std::nth_element(v, v + row_offset, v_end, CmpLess(this));
     187             :                 }
     188         223 :                 std::partial_sort(v + skip, v + row_end, v_end, CmpLess(this));
     189         223 :                 std::copy(v + row_offset, v + row_end, result);
     190         223 :                 delete[] v;
     191             :         } else {
     192        1337 :                 if (maxResults == nGames_) {
     193         557 :                         std::copy(fullMap_ + row_offset, fullMap_ + row_end, result);
     194             :                 } else {
     195         780 :                         size_t filterCount = 0;
     196         780 :                         size_t i = 0;
     197     1048040 :                         for (; filterCount < row_offset; i++) {
     198      523630 :                                 if (filter->get(fullMap_[i]) != 0)
     199      260520 :                                         filterCount++;
     200             :                         }
     201     1041160 :                         for (; filterCount != row_end; i++) {
     202      520190 :                                 if (filter->get(fullMap_[i]) != 0) {
     203      262080 :                                         *result++ = fullMap_[i];
     204      262080 :                                         filterCount++;
     205             :                                 }
     206             :                         }
     207             :                 }
     208             :         }
     209             : 
     210        1560 :         return row_end - row_offset;
     211             : }
     212             : 
     213        3120 : size_t SortCache::sortedPosition(gamenumT gameId, const HFilter& filter) const {
     214        3120 :         ASSERT(filter != 0 && filter->size() <= nGames_);
     215        3120 :         ASSERT(gameId < nGames_ && filter->get(gameId) != 0);
     216             : 
     217        3120 :         size_t res = 0;
     218        3120 :         if (valid_fullMap_) {
     219     2108392 :                 for (gamenumT i = 0; i < nGames_; i++) {
     220     2108392 :                         if (filter->get(fullMap_[i]) == 0)
     221      534346 :                                 continue;
     222     1574046 :                         if (fullMap_[i] == gameId)
     223        3086 :                                 break;
     224     1570960 :                         res++;
     225             :                 }
     226             :         } else {
     227          34 :                 CmpLess comp(this);
     228       45560 :                 for (gamenumT i = 0; i < nGames_; i++) {
     229       45526 :                         if (filter->get(i) == 0)
     230           0 :                                 continue;
     231       45526 :                         if (comp(i, gameId))
     232       21308 :                                 ++res;
     233             :                 }
     234             :         }
     235        3120 :         return res;
     236             : }
     237             : 
     238           0 : void SortCache::checkForChanges(gamenumT id) {
     239           0 :         th_interrupt();
     240           0 :         if (id >= nGames_) {
     241           0 :                 generateHashCache();
     242           0 :                 sortAsynchronously();
     243             :         } else {
     244           0 :                 hash_[id] = calcHash(id);
     245           0 :                 if (valid_fullMap_) {
     246           0 :                         gamenumT* begin = fullMap_;
     247           0 :                         gamenumT* end = fullMap_ + nGames_;
     248           0 :                         gamenumT* it = std::find(begin, end, id);
     249           0 :                         ASSERT(it != end);
     250             :                         // Reposition the game if necessary:
     251             :                         // - to the left if it compares less than the previous element;
     252             :                         // - to the right if the next element is lower.
     253           0 :                         CmpLess comp(this);
     254           0 :                         if (it != begin && comp(*it, *(it - 1))) {
     255           0 :                                 std::rotate(
     256             :                                         std::upper_bound(begin, it, *it, comp),
     257             :                                         it, it + 1);
     258           0 :                         } else if (it + 1 != end && comp(*(it + 1), *it)) {
     259           0 :                                 std::rotate(
     260             :                                         it, it + 1,
     261             :                                         std::lower_bound(it + 1, end, *it, comp));
     262             :                         }
     263           0 :                         ASSERT(std::is_sorted(begin, end, comp));
     264             :                 } else {
     265           0 :                         sortAsynchronously();
     266             :                 }
     267             :         }
     268           0 : }
     269             : 
     270             : /*
     271             :  * Calculate the hashes of all games and store them into @e hash_.
     272             :  */
     273          76 : void SortCache::generateHashCache() {
     274          76 :         ASSERT(th_ == nullptr);
     275             : 
     276          76 :         valid_fullMap_ = false;
     277          76 :         nGames_ = index_->GetNumGames();
     278             : 
     279             :         // Generate the hash table.
     280          76 :         delete[] hash_;
     281          76 :         hash_ = new uint32_t[nGames_];
     282      101840 :         for (gamenumT i = 0; i < nGames_; i++) {
     283      101764 :                 hash_[i] = calcHash(i);
     284             :         }
     285          76 : }
     286             : 
     287             : /*
     288             :  * Start a background thread that will sort the gameIds and will populate @e
     289             :  * fullMap_.
     290             :  */
     291          76 : void SortCache::sortAsynchronously() {
     292          76 :         ASSERT(th_ == nullptr);
     293             : 
     294             : #ifndef MULTITHREADING_OFF
     295          76 :         delete[] fullMap_;
     296          76 :         fullMap_ = new gamenumT[nGames_];
     297          76 :         th_ = new std::thread(&SortCache::th_sort, this);
     298             : #endif
     299          76 : }
     300             : 
     301             : /*
     302             :  * Compare two games according to @e criteria_.
     303             :  * The @e index_ is accessed only if the games' hashes are equal.
     304             :  * @param g1: the id of the first game.
     305             :  * @param g2: the id of the second game.
     306             :  * @returns true if @e g1 is ordered before @e g2.
     307             :  */
     308     4320165 : bool SortCache::CmpLess::operator()(gamenumT g1, gamenumT g2) const {
     309     4320165 :         ASSERT(g1 < sc_->nGames_ && g2 < sc_->nGames_);
     310             : 
     311     4320165 :         if (sc_->hash_[g1] != sc_->hash_[g2])
     312     2469334 :                 return sc_->hash_[g1] < sc_->hash_[g2];
     313     1850831 :         if (!sc_->partialHash_)
     314     1347705 :                 return g1 < g2;
     315             : 
     316      503126 :         int cmp = sc_->fullCompare(g1, g2);
     317      503126 :         if (cmp != 0)
     318      279202 :                 return cmp < 0;
     319             : 
     320      223924 :         return g1 < g2;
     321             : }
     322             : 
     323             : static const int RESULT_SORT[] = { 0, 3, 1, 2 };
     324      228576 : static int nameComp(const NameBase* nbase, nameT nt, idNumberT id1,
     325             :                     idNumberT id2) {
     326      228576 :         ASSERT(nbase != NULL);
     327      313871 :         return (id1 == id2) ? 0
     328       85295 :                             : strCaseCompare(nbase->GetName(nt, id1),
     329      228576 :                                              nbase->GetName(nt, id2));
     330             : }
     331             : 
     332             : /*
     333             :  * Compare two games according to @e criteria_.
     334             :  * @param left: the id of the first game.
     335             :  * @param right: the id of the second game.
     336             :  * @returns
     337             :  * - <0 if @e left is ordered before @e right.
     338             :  * - >0 if @e right is ordered before @e left.
     339             :  * - 0 otherwise.
     340             :  */
     341      503126 : int SortCache::fullCompare(gamenumT left, gamenumT right) const {
     342      503126 :         const IndexEntry *ie1 = index_->GetEntry(left);
     343      503126 :         const IndexEntry *ie2 = index_->GetEntry(right);
     344             : 
     345     1594206 :         for (const char* field = criteria_; *field != SORTING_sentinel; field += 2) {
     346             :                 int res;
     347     1370282 :                 switch (*field) {
     348       26022 :                 case SORTING_date:
     349       26022 :                         res = (int)ie1->GetDate() - (int)ie2->GetDate();
     350       26022 :                         break;
     351             : 
     352       31203 :                 case SORTING_year:
     353       31203 :                         res = (int)ie1->GetYear() - (int)ie2->GetYear();
     354       31203 :                         break;
     355             : 
     356       47273 :                 case SORTING_eco:
     357       47273 :                         res = (int)ie1->GetEcoCode() - (int)ie2->GetEcoCode();
     358       47514 :                         break;
     359             : 
     360       79296 :                 case SORTING_moveCount:
     361       79296 :                         res = (int)ie1->GetNumHalfMoves() - (int)ie2->GetNumHalfMoves();
     362       79296 :                         break;
     363             : 
     364       49753 :                 case SORTING_white:
     365       49753 :                         res = nameComp(nbase_, NAME_PLAYER, ie1->GetWhite(), ie2->GetWhite());
     366       49753 :                         break;
     367             : 
     368       88616 :                 case SORTING_black:
     369       88616 :                         res = nameComp(nbase_, NAME_PLAYER, ie1->GetBlack(), ie2->GetBlack());
     370       88616 :                         break;
     371             : 
     372       38598 :                 case SORTING_event:
     373       38598 :                         res = nameComp(nbase_, NAME_EVENT, ie1->GetEvent(), ie2->GetEvent());
     374       38598 :                         break;
     375             : 
     376       51609 :                 case SORTING_site:
     377       51609 :                         res = nameComp(nbase_, NAME_SITE, ie1->GetSite(), ie2->GetSite());
     378       51609 :                         break;
     379             : 
     380      113423 :                 case SORTING_round: {
     381      113423 :                         idNumberT id1 = ie1->GetRound();
     382      113423 :                         idNumberT id2 = ie2->GetRound();
     383      113423 :                         res = (id1 == id2)
     384      149353 :                                   ? 0
     385       35930 :                                   : strCompareRound(nbase_->GetName(NAME_ROUND, id1),
     386       35930 :                                                     nbase_->GetName(NAME_ROUND, id2));
     387      113423 :                         break;
     388             :                 }
     389             : 
     390       50202 :                 case SORTING_resultwin:
     391       50202 :                         res = (ie1->GetResult() == RESULT_White ? 1 : 0) - (ie2->GetResult() == RESULT_White ? 1 : 0);
     392       50202 :                         break;
     393             : 
     394       92358 :                 case SORTING_resultdraw:
     395       92358 :                         res = (ie1->GetResult() == RESULT_Draw ? 1 : 0) - (ie2->GetResult() == RESULT_Draw ? 1 : 0);
     396       92358 :                         break;
     397             : 
     398      144134 :                 case SORTING_resultloss:
     399      144134 :                         res = (ie1->GetResult() == RESULT_Black ? 1 : 0) - (ie2->GetResult() == RESULT_Black ? 1 : 0);
     400      144134 :                         break;
     401             : 
     402      105371 :                 case SORTING_result:
     403      105371 :                         res = RESULT_SORT[ie1->GetResult()] - RESULT_SORT[ie2->GetResult()];
     404      105371 :                         break;
     405             : 
     406       21885 :                 case SORTING_avgElo:  // Average Elo rating:
     407             :                         {
     408       21885 :                                 int r1 = ie1->GetWhiteElo(nbase_) + ie1->GetBlackElo(nbase_);
     409       21885 :                                 int r2 = ie2->GetWhiteElo(nbase_) + ie2->GetBlackElo(nbase_);
     410       21885 :                                 res = r1 - r2;
     411             :                         }
     412       21885 :                         break;
     413             : 
     414       91405 :                 case SORTING_country:  // Last 3 characters of site field:
     415             :                         {
     416       91405 :                                 const char* sOne = ie1->GetSiteName(nbase_);
     417       91405 :                                 const char* sTwo = ie2->GetSiteName(nbase_);
     418       91405 :                                 size_t slenOne = std::strlen(sOne);
     419       91405 :                                 size_t slenTwo = std::strlen(sTwo);
     420       91405 :                                 if (slenOne > 3) { sOne += slenOne - 3; }
     421       91405 :                                 if (slenTwo > 3) { sTwo += slenTwo - 3; }
     422       91405 :                                 res = strCaseCompare (sOne, sTwo);
     423             :                         }
     424       91405 :                         break;
     425             : 
     426       30355 :                 case SORTING_deleted:
     427       30355 :                         res = (int)ie1->GetDeleteFlag() - (int)ie2->GetDeleteFlag();
     428       30355 :                         break;
     429             : 
     430       52056 :                 case SORTING_eventdate:
     431       52056 :                         res = (int)ie1->GetEventDate() - (int)ie2->GetEventDate();
     432       52056 :                         break;
     433             : 
     434        7320 :                 case SORTING_whiteelo:
     435        7320 :                         res = (int)ie1->GetWhiteElo(nbase_) - (int)ie2->GetWhiteElo(nbase_);
     436        7320 :                         break;
     437             : 
     438       43701 :                 case SORTING_blackelo:
     439       43701 :                         res = (int)ie1->GetBlackElo(nbase_) - (int)ie2->GetBlackElo(nbase_);
     440       43701 :                         break;
     441             : 
     442       62371 :                 case SORTING_commentcount:
     443       62371 :                         res = (int) ie1->GetCommentCount() - (int) ie2->GetCommentCount();
     444       62371 :                         break;
     445             : 
     446       68038 :                 case SORTING_varcount:
     447       68038 :                         res = (int) ie1->GetVariationCount() - (int) ie2->GetVariationCount();
     448       68038 :                         break;
     449             : 
     450       44807 :                 case SORTING_nagcount:
     451       44807 :                         res = (int) ie1->GetNagCount() - (int) ie2->GetNagCount();
     452       44807 :                         break;
     453             : 
     454       13217 :                 case SORTING_rating:
     455       13217 :                         res = (int)ie1->GetRating(nbase_) - (int)ie2->GetRating(nbase_);
     456       13217 :                         break;
     457             : 
     458       17269 :                 case SORTING_number:
     459       17269 :                         res = (int) left - (int) right;
     460       17269 :                         break;
     461             : 
     462           0 :                 default:    // Should never happen:
     463           0 :                         ASSERT(0);
     464             :                         return 0;
     465             :                 }
     466             : 
     467     1370523 :                 if (res != 0)
     468      279202 :                         return *(field + 1) ? -res : res;
     469             :         }
     470             : 
     471      223924 :     return 0;
     472             : }
     473             : 
     474             : /*
     475             :  * Calculate an order-preserving hash corresponding to the current criteria.
     476             :  * @param gameId: the id of the game whose hash should be calculated.
     477             :  * @returns the hash value.
     478             :  */
     479      101764 : uint32_t SortCache::calcHash(gamenumT gameId) {
     480      101764 :         uint64_t retValue = 0;
     481      101764 :         const size_t nHashBits = 32;
     482      101764 :         size_t totalBitsUsed = 0;
     483      101764 :         const IndexEntry* ie = index_->GetEntry(gameId);
     484             : 
     485      191477 :         for (const char* field = criteria_; *field != SORTING_sentinel; ++field) {
     486             :                 uint32_t value;
     487             :                 size_t bitsUsed;
     488      147290 :                 switch (*field) {
     489        5356 :                         case SORTING_white:
     490        5356 :                                 value = strStartHash(ie->GetWhiteName(nbase_));
     491        5356 :                                 bitsUsed = nHashBits;
     492        5356 :                                 partialHash_ = true;
     493        5356 :                                 break;
     494        4017 :                         case SORTING_black:
     495        4017 :                                 value = strStartHash(ie->GetBlackName(nbase_));
     496        4017 :                                 bitsUsed = nHashBits;
     497        4017 :                                 partialHash_ = true;
     498        4017 :                                 break;
     499        5356 :                         case SORTING_site:
     500        5356 :                                 value = strStartHash(ie->GetSiteName(nbase_));
     501        5356 :                                 bitsUsed = nHashBits;
     502        5356 :                                 partialHash_ = true;
     503        5356 :                                 break;
     504        6695 :                         case SORTING_event:
     505        6695 :                                 value = strStartHash(ie->GetEventName(nbase_));
     506        6695 :                                 bitsUsed = nHashBits;
     507        6695 :                                 partialHash_ = true;
     508        6695 :                                 break;
     509        4017 :                         case SORTING_round:
     510        4017 :                                 value = strGetUnsigned(ie->GetRoundName(nbase_));
     511        4017 :                                 bitsUsed = nHashBits;
     512        4017 :                                 partialHash_ = true;
     513        4017 :                                 break;
     514        8034 :                         case SORTING_country:
     515             :                         {
     516        8034 :                                 const char *scountry = ie->GetSiteName (nbase_);
     517        8034 :                                 size_t slen = std::strlen(scountry);
     518        8034 :                                 if (slen > 3) 
     519        7452 :                                         scountry += slen - 3;
     520        8034 :                                 value = strStartHash(scountry);
     521        8034 :                                 bitsUsed = nHashBits;
     522        8034 :                                 partialHash_ = true;
     523        8034 :                                 break;
     524             :                         }
     525        9373 :                         case SORTING_date:
     526        9373 :                                 value = ie->GetDate();
     527        9373 :                                 bitsUsed = 24;
     528        9373 :                                 break;
     529        4017 :                         case SORTING_eventdate:
     530        4017 :                                 value = ie->GetEventDate();
     531        4017 :                                 bitsUsed = 32;
     532        4017 :                                 break;
     533        4017 :                         case SORTING_year:
     534        4017 :                                 value = ie->GetYear();
     535        4017 :                                 bitsUsed = 16;
     536        4017 :                                 break;
     537        5356 :                         case SORTING_whiteelo:
     538        5356 :                                 value = ie->GetWhiteElo(nbase_);
     539        5356 :                                 bitsUsed = 16;
     540        5356 :                                 break;
     541        8034 :                         case SORTING_blackelo:
     542        8034 :                                 value = ie->GetBlackElo(nbase_);
     543        8034 :                                 bitsUsed = 16;
     544        8034 :                                 break;
     545        4017 :                         case SORTING_avgElo:
     546        4017 :                                 value = ie->GetWhiteElo(nbase_) + ie->GetBlackElo(nbase_);
     547        4017 :                                 bitsUsed = 16;
     548        4017 :                                 break;
     549        9373 :                         case SORTING_result:
     550        9373 :                                 value = RESULT_SORT[ie->GetResult()];
     551        9373 :                                 bitsUsed = 8;
     552        9373 :                                 break;
     553        6695 :                         case SORTING_resultwin:
     554        6695 :                                 value = ie->GetResult() == RESULT_White ? 1 : 0;
     555        6695 :                                 bitsUsed = 8;
     556        6695 :                                 break;
     557        8034 :                         case SORTING_resultdraw:
     558        8034 :                                 value = ie->GetResult() == RESULT_Draw ? 1 : 0;
     559        8034 :                                 bitsUsed = 8;
     560        8034 :                                 break;
     561       12051 :                         case SORTING_resultloss:
     562       12051 :                                 value = ie->GetResult() == RESULT_Black ? 1 : 0;
     563       12051 :                                 bitsUsed = 8;
     564       12051 :                                 break;
     565        9373 :                         case SORTING_moveCount:
     566        9373 :                                 value = ie->GetNumHalfMoves();
     567        9373 :                                 bitsUsed = 16;
     568        9373 :                                 break;
     569        6695 :                         case SORTING_eco:
     570        6695 :                                 value = ie->GetEcoCode();
     571        6695 :                                 bitsUsed = 16;
     572        6695 :                                 break;
     573        5356 :                         case SORTING_commentcount:
     574        5356 :                                 value = ie->GetCommentCount();
     575        5356 :                                 bitsUsed = 16;
     576        5356 :                                 break;
     577        5356 :                         case SORTING_varcount:
     578        5356 :                                 value = ie->GetVariationCount();
     579        5356 :                                 bitsUsed = 16;
     580        5356 :                                 break;
     581        2678 :                         case SORTING_nagcount:
     582        2678 :                                 value = ie->GetNagCount();
     583        2678 :                                 bitsUsed = 16;
     584        2678 :                                 break;
     585        4017 :                         case SORTING_deleted:
     586        4017 :                                 value = (ie->GetDeleteFlag() ? 1 : 0);
     587        4017 :                                 bitsUsed = 8;
     588        4017 :                                 break;
     589        5356 :                         case SORTING_rating:
     590        5356 :                                 value = ie->GetRating(nbase_);
     591        5356 :                                 bitsUsed = 8;
     592        5356 :                                 break;
     593        4017 :                         case SORTING_number:
     594        4017 :                                 value = gameId;
     595        4017 :                                 bitsUsed = 32;
     596        4017 :                                 break;
     597           0 :                         default:    // Should never happen:
     598           0 :                                 ASSERT(0);
     599             :                                 partialHash_ = true;
     600             :                                 return 0;
     601             :                 }
     602             : 
     603             :                 // If reverse order, just negate the cache value
     604      147290 :                 if (*++field) {
     605       83018 :                         value = ~value;
     606       83018 :                         if (sizeof(value) * 8 > bitsUsed) {
     607             :                                 // Clear the unused top bits
     608       57577 :                                 value <<= sizeof(value) * 8 - bitsUsed;
     609       57577 :                                 value >>= sizeof(value) * 8 - bitsUsed;
     610             :                         }
     611             :                 }
     612             :                 // Combine with previous hash value
     613      147290 :                 retValue <<= bitsUsed;
     614      147290 :                 retValue |= value;
     615      147290 :                 totalBitsUsed += bitsUsed;
     616             : 
     617             :                 // If not all search attributes fit, then it is a partial hash
     618      147290 :                 if (totalBitsUsed > nHashBits) {
     619       18746 :                         retValue >>= totalBitsUsed - nHashBits;
     620       18746 :                         partialHash_ = true;
     621       18746 :                         break;
     622             :                 }
     623      128544 :                 if (totalBitsUsed == nHashBits) {
     624       38831 :                         if (*(field + 1) != SORTING_sentinel)
     625       17407 :                                 partialHash_ = true;
     626       38831 :                         break;
     627             :                 }
     628             :         }
     629             : 
     630      101764 :         return static_cast<uint32_t>(retValue);
     631             : }

Generated by: LCOV version 1.13