LCOV - code coverage report
Current view: top level - src - scidbase.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 218 465 46.9 %
Date: 2019-01-29 11:06:41 Functions: 24 34 70.6 %

          Line data    Source code
       1             : /*
       2             : * Copyright (C) 2014-2016  Fulvio Benini
       3             : 
       4             : * This file is part of Scid (Shane's Chess Information Database).
       5             : *
       6             : * Scid is free software: you can redistribute it and/or modify
       7             : * it under the terms of the GNU General Public License as published by
       8             : * the Free Software Foundation.
       9             : *
      10             : * Scid is distributed in the hope that it will be useful,
      11             : * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             : * GNU General Public License for more details.
      14             : *
      15             : * You should have received a copy of the GNU General Public License
      16             : * along with Scid.  If not, see <http://www.gnu.org/licenses/>.
      17             : */
      18             : 
      19             : #include "scidbase.h"
      20             : #include "codec_memory.h"
      21             : #include "codec_pgn.h"
      22             : #include "codec_scid4.h"
      23             : #include "common.h"
      24             : #include "sortcache.h"
      25             : #include "stored.h"
      26             : #include <algorithm>
      27             : #include <math.h>
      28             : 
      29             : std::pair<ICodecDatabase*, errorT>
      30          50 : ICodecDatabase::open(Codec codec, fileModeT fMode, const char* filename,
      31             :                      const Progress& progress, Index* idx, NameBase* nb) {
      32          50 :         auto createCodec = [](auto codec) -> ICodecDatabase* {
      33          50 :                 switch (codec) {
      34          15 :                 case ICodecDatabase::MEMORY:
      35          15 :                         return new CodecMemory();
      36          16 :                 case ICodecDatabase::SCID4:
      37          16 :                         return new CodecSCID4();
      38          19 :                 case ICodecDatabase::PGN:
      39          19 :                         return new CodecPgn();
      40             :                 }
      41           0 :                 ASSERT(0);
      42             :                 return nullptr;
      43             :         };
      44             : 
      45          50 :         auto obj = createCodec(codec);
      46          50 :         auto err = obj->dyn_open(fMode, filename, progress, idx, nb);
      47          50 :         if (err != OK && err != ERROR_NameDataLoss) {
      48           9 :                 delete obj;
      49           9 :                 obj = nullptr;
      50             :         }
      51          50 :         return {obj, err};
      52             : }
      53             : 
      54          14 : scidBaseT::scidBaseT() {
      55          14 :         idx = new Index;
      56          14 :         nb_ = new NameBase;
      57          14 :         game = new Game;
      58          14 :         gameNumber = -1;
      59          14 :         gameAltered = false;
      60          14 :         inUse = false;
      61          14 :         tree.moveCount = tree.totalCount = 0;
      62          14 :         fileMode_ = FMODE_None;
      63          14 :         bbuf = new ByteBuffer(BBUF_SIZE);
      64          14 :         dbFilter = new Filter(0);
      65          14 :         treeFilter = new Filter(0);
      66          14 :         duplicates_ = NULL;
      67          14 :         stats_ = NULL;
      68          14 : }
      69             : 
      70          28 : scidBaseT::~scidBaseT() {
      71          14 :         if (inUse)
      72          14 :                 Close();
      73             : 
      74          14 :         delete[] duplicates_;
      75          14 :         delete idx;
      76          14 :         delete nb_;
      77          14 :         delete game;
      78          14 :         delete bbuf;
      79          14 :         delete stats_;
      80          14 :         delete dbFilter;
      81          14 :         delete treeFilter;
      82          14 : }
      83             : 
      84          14 : errorT scidBaseT::Open(ICodecDatabase::Codec dbtype, fileModeT fMode,
      85             :                        const char* filename, const Progress& progress) {
      86          14 :         if (inUse)
      87           0 :                 return ERROR_FileInUse;
      88          14 :         if (filename == 0)
      89           7 :                 filename = "";
      90             : 
      91          14 :         auto obj = ICodecDatabase::open(dbtype, fMode, filename, progress, idx, nb_);
      92          14 :         if (obj.first) {
      93          14 :                 codec_.reset(obj.first);
      94          14 :                 inUse = true;
      95          14 :                 fileMode_ = (fMode == FMODE_Create) ? FMODE_Both : fMode;
      96          14 :                 fileName_ = filename;
      97          14 :                 gameNumber = -1;
      98             : 
      99             :                 // Initialize the filters: all the games are included by default.
     100          14 :                 dbFilter->Init(numGames());
     101          14 :                 treeFilter->Init(numGames());
     102          14 :                 ASSERT(filters_.empty());
     103             : 
     104             :                 // Default treeCache size: 250
     105          14 :                 treeCache.CacheResize(250);
     106             :         } else {
     107           0 :                 idx->Close();
     108           0 :                 nb_->Clear();
     109             :         }
     110             : 
     111          14 :         return obj.second;
     112             : }
     113             : 
     114          14 : errorT scidBaseT::Close () {
     115          14 :         ASSERT(inUse);
     116             : 
     117          16 :         for (auto& sortCache : sortCaches_) {
     118           2 :                 delete sortCache.second;
     119             :         }
     120          14 :         sortCaches_.clear();
     121             : 
     122          14 :         errorT errIdx = idx->Close();
     123          14 :         nb_->Clear();
     124          14 :         codec_ = nullptr;
     125             : 
     126          14 :         clear();
     127          14 :         game->Clear();
     128          14 :         fileMode_ = FMODE_None;
     129          14 :         fileName_ = "<empty>";
     130          14 :         gameNumber = -1;
     131          14 :         gameAltered = false;
     132          14 :         dbFilter->Init(0);
     133          14 :         treeFilter->Init(0);
     134          14 :         for (size_t i=0, n = filters_.size(); i < n; i++) delete filters_[i].second;
     135          14 :         filters_.clear();
     136          14 :         inUse = false;
     137             : 
     138          14 :         return errIdx;
     139             : }
     140             : 
     141             : 
     142          25 : void scidBaseT::clear() {
     143          25 :         if (stats_ != NULL) { delete stats_; stats_ = NULL;}
     144          25 :         if (duplicates_ != NULL) { delete[] duplicates_; duplicates_ = NULL; }
     145          25 :         treeCache.Clear();
     146         125 :         for (nameT nt = NAME_PLAYER; nt < NUM_NAME_TYPES; nt++) {
     147         100 :                 nameFreq_[nt].resize(0);
     148             :         }
     149          25 : }
     150             : 
     151          11 : void scidBaseT::beginTransaction() {
     152          11 :         for (auto& sortCache : sortCaches_) {
     153           0 :                 sortCache.second->prepareForChanges();
     154             :         }
     155          11 : }
     156             : 
     157          11 : errorT scidBaseT::endTransaction(gamenumT gNum) {
     158          11 :         clear();
     159          11 :         errorT res = codec_->flush();
     160             : 
     161          11 :         auto n_games = numGames();
     162          11 :         if (dbFilter->Size() != n_games) {
     163          10 :                 dbFilter->Resize(n_games);
     164          10 :                 treeFilter->Resize(n_games);
     165          10 :                 for (auto& filter : filters_) {
     166           0 :                         filter.second->Resize(n_games);
     167             :                 }
     168             :         }
     169             : 
     170          11 :         for (auto& sortCache : sortCaches_) {
     171           0 :                 sortCache.second->checkForChanges(gNum);
     172             :         }
     173             : 
     174          11 :         return res;
     175             : }
     176             : 
     177           9 : errorT scidBaseT::saveGame(Game* game, gamenumT replacedGameId) {
     178           9 :         beginTransaction();
     179           9 :         errorT err1 = saveGameHelper(game, replacedGameId);
     180           9 :         errorT err2 = endTransaction(replacedGameId);
     181           9 :         return (err1 != OK) ? err1 : err2;
     182             : }
     183             : 
     184           9 : errorT scidBaseT::saveGameHelper(Game* game, gamenumT gameId) {
     185           9 :         if (isReadOnly())
     186           0 :                 return ERROR_FileReadOnly;
     187             : 
     188           9 :         if (gameId < numGames())
     189           1 :                 return codec_->saveGame(game, gameId);
     190             : 
     191           8 :         return codec_->addGame(game);
     192             : }
     193             : 
     194           3 : errorT scidBaseT::importGame(const scidBaseT* srcBase, uint gNum) {
     195           3 :         if (srcBase == this) return ERROR_BadArg;
     196           2 :         if (isReadOnly()) return ERROR_FileReadOnly;
     197           2 :         if (gNum >= srcBase->numGames()) return ERROR_BadArg;
     198             : 
     199           1 :         beginTransaction();
     200           1 :         errorT err = importGameHelper(srcBase, gNum);
     201           1 :         errorT errClear = endTransaction();
     202           1 :         return (err == OK) ? errClear : err;
     203             : }
     204             : 
     205           2 : errorT scidBaseT::importGames(const scidBaseT* srcBase, const HFilter& filter, const Progress& progress) {
     206           2 :         ASSERT(srcBase != 0);
     207           2 :         ASSERT(filter != 0);
     208           2 :         if (srcBase == this) return ERROR_BadArg;
     209           1 :         if (isReadOnly()) return ERROR_FileReadOnly;
     210             : 
     211           1 :         beginTransaction();
     212           1 :         errorT err = OK;
     213           1 :         size_t iProgress = 0;
     214           1 :         size_t totGames = filter->size();
     215           4 :         for (gamenumT gNum = 0, n = srcBase->numGames(); gNum < n; gNum++) {
     216           3 :                 if (filter.get(gNum) == 0) continue;
     217           2 :                 err = importGameHelper(srcBase, gNum);
     218           2 :                 if (err != OK) break;
     219           2 :                 if (++iProgress % 8192 == 0) {
     220           0 :                         if (!progress.report(iProgress, totGames)) break;
     221             :                 }
     222             :         }
     223           1 :         errorT errClear = endTransaction();
     224           1 :         return (err == OK) ? errClear : err;
     225             : }
     226             : 
     227           3 : errorT scidBaseT::importGameHelper(const scidBaseT* srcBase, gamenumT gNum) {
     228           3 :         auto srcIe = srcBase->getIndexEntry(gNum);
     229           3 :         auto dataSz = srcIe->GetLength();
     230           3 :         auto data = srcBase->codec_->getGameData(srcIe->GetOffset(), dataSz);
     231           3 :         if (data == nullptr)
     232           0 :                 return ERROR_FileRead;
     233             : 
     234           3 :         return codec_->addGame(srcIe, srcBase->getNameBase(), data, dataSz);
     235             : }
     236             : 
     237           0 : errorT scidBaseT::importGames(ICodecDatabase::Codec dbtype,
     238             :                               const char* filename, const Progress& progress,
     239             :                               std::string& errorMsg) {
     240           0 :         ASSERT(dbtype == ICodecDatabase::PGN);
     241             : 
     242           0 :         if (isReadOnly())
     243           0 :                 return ERROR_FileReadOnly;
     244             : 
     245           0 :         beginTransaction();
     246             : 
     247           0 :         CodecPgn pgn;
     248           0 :         auto res = pgn.open(filename, FMODE_ReadOnly);
     249           0 :         if (res == OK) {
     250           0 :                 res = CodecPgn::parseGames(
     251           0 :                     progress, pgn, [&](Game& game) { return codec_->addGame(&game); });
     252           0 :                 errorMsg = pgn.parseErrors();
     253             :         }
     254             : 
     255           0 :         auto res_endTrans = endTransaction();
     256           0 :         return (res != OK) ? res : res_endTrans;
     257             : }
     258             : 
     259             : /**
     260             :  * Filters
     261             :  */
     262         102 : std::string scidBaseT::newFilter() {
     263         102 :         std::string newname = (filters_.size() == 0)
     264             :                 ? "a_"
     265         102 :                 : filters_.back().first;
     266         102 :         if (newname[0] == 'z') {
     267           3 :                 newname = 'a' + newname;
     268             :         } else {
     269          99 :                 newname = ++(newname[0]) + newname.substr(1);
     270             :         }
     271         102 :         filters_.push_back(std::make_pair(newname, new Filter(numGames())));
     272         102 :         return newname;
     273             : }
     274             : 
     275       17395 : std::string scidBaseT::composeFilter(const std::string& mainFilter,
     276             :                                      const std::string& maskFilter) const {
     277       17395 :         std::string res;
     278       17395 :         if (mainFilter.empty()) return res;
     279             : 
     280       17265 :         if (mainFilter[0] != '+') {
     281        8470 :                 res = mainFilter;
     282             :         } else {
     283        8795 :                 size_t maskName = mainFilter.find('+', 1);
     284        8795 :                 if (maskName != std::string::npos)
     285        8405 :                         res = mainFilter.substr(1, maskName - 1);
     286             :         }
     287             : 
     288       17265 :         if (!maskFilter.empty()) {
     289        9120 :                 res = '+' + res + "+" + maskFilter;
     290             :         }
     291             : 
     292       17265 :         if (getFilter(res) == 0) res.clear();
     293       17265 :         return res;
     294             : }
     295             : 
     296          29 : void scidBaseT::deleteFilter(const char* filterId) {
     297        1404 :         for (size_t i = 0, n = filters_.size(); i < n; i++) {
     298        1400 :                 if (filters_[i].first == filterId) {
     299          25 :                         delete filters_[i].second;
     300          25 :                         filters_.erase(filters_.begin() + i);
     301          25 :                         break;
     302             :                 }
     303             :         }
     304          29 : }
     305             : 
     306       53686 : Filter* scidBaseT::fetchFilter(const std::string& filterId) const {
     307       53686 :         if (filterId == "dbfilter") return dbFilter;
     308       53162 :         if (filterId == "tree") return treeFilter;
     309             : 
     310     2084108 :         for (size_t i = 0, n = filters_.size(); i < n; i++) {
     311     2078453 :                 if (filterId == filters_[i].first)
     312       45819 :                         return filters_[i].second;
     313             :         }
     314        5655 :         return 0;
     315             : }
     316             : 
     317       36551 : HFilter scidBaseT::getFilterHelper(const std::string& filterId,
     318             :                                    bool unmasked) const {
     319       36551 :         Filter* main = 0;
     320       36551 :         const Filter* mask = 0;
     321       36551 :         if (filterId.empty() || filterId[0] != '+') {
     322       18636 :                 main = fetchFilter(filterId);
     323             :         } else {
     324       17915 :                 size_t maskName = filterId.find('+', 1);
     325       17915 :                 if (maskName != std::string::npos) {
     326       17525 :                         main = fetchFilter(filterId.substr(1, maskName - 1));
     327       17525 :                         if (!unmasked) mask = fetchFilter(filterId.substr(maskName + 1));
     328             :                 }
     329             :         }
     330       36551 :         return HFilter(main, mask);
     331             : }
     332             : 
     333             : /**
     334             :  * Statistics
     335             :  */
     336           0 : const scidBaseT::Stats& scidBaseT::getStats() const {
     337           0 :         if (stats_ == NULL) stats_ = new scidBaseT::Stats(this);
     338           0 :         return *stats_;
     339             : }
     340             : 
     341           0 : scidBaseT::Stats::Eco::Eco()
     342           0 : : count(0) {
     343           0 :         std::fill_n(results, NUM_RESULT_TYPES, 0);
     344           0 : }
     345             : 
     346           0 : scidBaseT::Stats::Stats(const scidBaseT* dbase) {
     347           0 :         std::fill(flagCount, flagCount + IndexEntry::IDX_NUM_FLAGS, 0);
     348           0 :         minDate = ZERO_DATE;
     349           0 :         maxDate = ZERO_DATE;
     350           0 :         nYears = 0;
     351           0 :         sumYears = 0;
     352           0 :         std::fill_n(nResults, NUM_RESULT_TYPES, 0);
     353           0 :         nRatings = 0;
     354           0 :         sumRatings = 0;
     355           0 :         minRating = 0;
     356           0 :         maxRating = 0;
     357             : 
     358             :         // Read stats from index entry of each game:
     359           0 :         for (gamenumT gnum=0, n = dbase->numGames(); gnum < n; gnum++) {
     360           0 :                 const IndexEntry* ie = dbase->getIndexEntry(gnum);
     361           0 :                 nResults[ie->GetResult()]++;
     362           0 :                 eloT elo = ie->GetWhiteElo();
     363           0 :                 if (elo > 0) {
     364           0 :                         nRatings++;
     365           0 :                         sumRatings += elo;
     366           0 :                         if (minRating == 0) { minRating = elo; }
     367           0 :                         if (elo < minRating) { minRating = elo; }
     368           0 :                         if (elo > maxRating) { maxRating = elo; }
     369             :                 }
     370           0 :                 elo = ie->GetBlackElo();
     371           0 :                 if (elo > 0) {
     372           0 :                         nRatings++;
     373           0 :                         sumRatings += elo;
     374           0 :                         if (minRating == 0) { minRating = elo; }
     375           0 :                         if (elo < minRating) { minRating = elo; }
     376           0 :                         if (elo > maxRating) { maxRating = elo; }
     377             :                 }
     378           0 :                 dateT date = ie->GetDate();
     379           0 :                 if (gnum == 0) {
     380           0 :                         maxDate = minDate = date;
     381             :                 }
     382           0 :                 if (date_GetYear(date) > 0) {
     383           0 :                         if (date < minDate) { minDate = date; }
     384           0 :                         if (date > maxDate) { maxDate = date; }
     385           0 :                         nYears++;
     386           0 :                         sumYears += date_GetYear (date);
     387             :                 }
     388             : 
     389           0 :                 for (uint flag = 0; flag < IndexEntry::IDX_NUM_FLAGS; flag++) {
     390           0 :                         bool value = ie->GetFlag (1 << flag);
     391           0 :                         if (value) {
     392           0 :                                 flagCount[flag]++;
     393             :                         }
     394             :                 }
     395             : 
     396           0 :                 resultT result = ie->GetResult();
     397           0 :                 ecoT eco = ie->GetEcoCode();
     398           0 :                 if (eco == 0) {
     399           0 :                         ecoEmpty_.count++;
     400           0 :                         ecoEmpty_.results[result]++;
     401             :                 } else {
     402           0 :                         ecoValid_.count++;
     403           0 :                         ecoValid_.results[result]++;
     404           0 :                         eco = eco_Reduce(eco);
     405           0 :                         ecoStats_[eco].count++;
     406           0 :                         ecoStats_[eco].results[result]++;
     407           0 :                         eco /= 27;
     408           0 :                         ecoGroup3_[eco].count++;
     409           0 :                         ecoGroup3_[eco].results[result]++;
     410           0 :                         eco /= 10;
     411           0 :                         ecoGroup2_[eco].count++;
     412           0 :                         ecoGroup2_[eco].results[result]++;
     413           0 :                         eco /= 10;
     414           0 :                         ecoGroup1_[eco].count++;
     415           0 :                         ecoGroup1_[eco].results[result]++;
     416             :                 }
     417             :         }
     418           0 : }
     419             : 
     420           0 : const scidBaseT::Stats::Eco* scidBaseT::Stats::getEcoStats(const char* ecoStr) const {
     421           0 :         ASSERT(ecoStr != 0);
     422             : 
     423           0 :         if (*ecoStr == 0) return &ecoValid_;
     424             : 
     425           0 :         ecoT eco = eco_FromString(ecoStr);
     426           0 :         if (eco == 0) return 0;
     427           0 :         eco = eco_Reduce(eco);
     428             : 
     429           0 :         switch(strlen(ecoStr)) {
     430           0 :         case 0:
     431           0 :                 return &ecoValid_;
     432           0 :         case 1:
     433           0 :                 return &(ecoGroup1_[eco / 2700]);
     434           0 :         case 2:
     435           0 :                 return &(ecoGroup2_[eco / 270]);
     436           0 :         case 3:
     437           0 :                 return &(ecoGroup3_[eco / 27]);
     438           0 :         case 4:
     439             :         case 5:
     440           0 :                 return &(ecoStats_[eco]);
     441             :         }
     442             : 
     443           0 :         return 0;
     444             : }
     445             : 
     446             : 
     447             : 
     448             : double scidBaseT::TreeStat::expVect_[1600];
     449             : 
     450           0 : scidBaseT::TreeStat::TreeStat()
     451           0 : : toMove(NOCOLOR), resultW(0), resultD(0), resultB(0), exp(0), ngames(0), nexp(0)
     452             : {
     453           0 :         if (TreeStat::expVect_[0] == 0) {
     454           0 :                 for (int i=-800; i < 800; i++) TreeStat::expVect_[i+800] = 1/(1 + pow(10, i/400.0));
     455             :         }
     456           0 : }
     457             : 
     458           0 : std::vector<scidBaseT::TreeStat> scidBaseT::getTreeStat(const HFilter& filter) {
     459           0 :         ASSERT(filter != 0);
     460             : 
     461           0 :         std::vector<scidBaseT::TreeStat> res;
     462           0 :         std::vector<FullMove> v;
     463           0 :         auto nb = getNameBase();
     464           0 :         for (gamenumT gnum = 0, n = numGames(); gnum < n; gnum++) {
     465           0 :                 uint ply = filter.get(gnum);
     466           0 :                 if (ply == 0) continue;
     467           0 :                 else ply--;
     468             : 
     469           0 :                 const IndexEntry* ie = getIndexEntry(gnum);
     470           0 :                 FullMove move = StoredLine::getMove(ie->GetStoredLineCode(), ply);
     471           0 :                 if (!move)
     472           0 :                         move = getGame(ie).getMove(ply);
     473             : 
     474           0 :                 size_t i = 0;
     475           0 :                 while (i < v.size() && v[i] != move) i++;
     476           0 :                 if (i == v.size()) {
     477           0 :                         v.push_back(move);
     478           0 :                         res.push_back(scidBaseT::TreeStat());
     479             :                 }
     480           0 :                 res[i].add(ie->GetResult(), ie->GetWhiteElo(nb), ie->GetBlackElo(nb));
     481             :         }
     482             : 
     483           0 :         for (size_t i = 0, n = v.size(); i < n; i++) {
     484           0 :                 res[i].SAN = !v[i] ? "[end]" : v[i].getSAN(&(res[i].toMove));
     485             :         }
     486             : 
     487           0 :         std::sort(res.begin(), res.end());
     488           0 :         return res;
     489             : }
     490             : 
     491           0 : errorT scidBaseT::getCompactStat(unsigned long long* n_deleted,
     492             :                                  unsigned long long* n_unused,
     493             :                                  unsigned long long* n_sparse,
     494             :                                  unsigned long long* n_badNameId) {
     495           0 :         std::vector<uint> nbFreq[NUM_NAME_TYPES];
     496           0 :         for (nameT n = NAME_PLAYER; n < NUM_NAME_TYPES; n++) {
     497           0 :                 nbFreq[n].resize(getNameBase()->GetNumNames(n), 0);
     498             :         }
     499             : 
     500           0 :         uint64_t last_offset = 0;
     501           0 :         *n_sparse = 0;
     502           0 :         *n_deleted = 0;
     503           0 :         for (gamenumT i=0, n = numGames(); i < n; i++) {
     504           0 :                 const IndexEntry* ie = getIndexEntry (i);
     505           0 :                 if (ie->GetDeleteFlag()) { *n_deleted += 1; continue; }
     506             : 
     507           0 :                 auto offset = ie->GetOffset();
     508           0 :                 if (offset < last_offset) *n_sparse += 1;
     509           0 :                 last_offset = offset;
     510             : 
     511           0 :                 nbFreq[NAME_PLAYER][ie->GetWhite()] += 1;
     512           0 :                 nbFreq[NAME_PLAYER][ie->GetBlack()] += 1;
     513           0 :                 nbFreq[NAME_EVENT][ie->GetEvent()] += 1;
     514           0 :                 nbFreq[NAME_SITE][ie->GetSite()] += 1;
     515           0 :                 nbFreq[NAME_ROUND][ie->GetRound()] += 1;
     516             :         }
     517             : 
     518           0 :         *n_unused = 0;
     519           0 :         for (nameT n = NAME_PLAYER; n < NUM_NAME_TYPES; n++) {
     520           0 :                 *n_unused += std::count(nbFreq[n].begin(), nbFreq[n].end(), 0);
     521             :         }
     522             : 
     523           0 :         *n_badNameId = idx->GetBadNameIdCount();
     524           0 :         return OK;
     525             : }
     526             : 
     527           0 : errorT scidBaseT::compact(const Progress& progress) {
     528           0 :         std::vector<std::string> filenames = codec_->getFilenames();
     529           0 :         if (filenames.empty()) return ERROR_CodecUnsupFeat;
     530             : 
     531           0 :         if (fileMode_ != FMODE_Both) {
     532             :                 //Older scid version to be upgraded are opened read only
     533           0 :                 if (idx->GetVersion() == SCID_VERSION) return ERROR_FileMode;
     534             :         }
     535             : 
     536             :         //1) Create a new temporary database
     537           0 :         std::string filename = fileName_;
     538           0 :         std::string tmpfile = filename + "__COMPACT__";
     539           0 :         ICodecDatabase::Codec dbtype = codec_->getType();
     540           0 :         scidBaseT tmp;
     541           0 :         errorT err_Create = tmp.Open(dbtype, FMODE_Create, tmpfile.c_str());
     542           0 :         if (err_Create != OK) return err_Create;
     543             : 
     544             :         //2) Copy the Index Header
     545           0 :         tmp.beginTransaction();
     546           0 :         tmp.idx->copyHeaderInfo(*idx);
     547           0 :         gamenumT autoloadOld = idx->GetAutoLoad();
     548           0 :         gamenumT autoloadNew = 1;
     549             : 
     550             :         //3) Create the list of games to be copied
     551           0 :         std::vector< std::pair<uint64_t, gamenumT> > sort;
     552           0 :         uint n_deleted = 0;
     553           0 :         for (gamenumT i = 0, n = numGames(); i < n; i++) {
     554           0 :                 const IndexEntry* ie = getIndexEntry(i);
     555           0 :                 if (ie->GetDeleteFlag()) {
     556           0 :                         n_deleted++;
     557           0 :                         continue;
     558             :                 }
     559           0 :                 uint64_t order = static_cast<uint64_t>(ie->GetStoredLineCode()) << 56;
     560           0 :                 const byte* hp = ie->GetHomePawnData();
     561           0 :                 order |= static_cast<uint64_t>(hp[0]) << 48;
     562           0 :                 order |= static_cast<uint64_t>(hp[1]) << 40;
     563           0 :                 order |= static_cast<uint64_t>(hp[2]) << 32;
     564           0 :                 order |= static_cast<uint64_t>(hp[3]) << 24;
     565           0 :                 order |= ie->GetFinalMatSig() & 0xFFFFFF;
     566           0 :                 sort.emplace_back(order, i);
     567             :         }
     568           0 :         std::stable_sort(sort.begin(), sort.end());
     569             : 
     570             :         //4) Copy the games
     571           0 :         uint iProgress = 0;
     572           0 :         bool err_UserCancel = false;
     573           0 :         errorT err_AddGame = OK;
     574           0 :         for (auto it = sort.cbegin(); it != sort.cend(); ++it) {
     575           0 :                 err_AddGame = tmp.importGameHelper(this, it->second);
     576           0 :                 if (err_AddGame != OK) break;
     577             : 
     578           0 :                 gamenumT oldGnum = it->second + 1;
     579           0 :                 if (oldGnum == autoloadOld) autoloadNew = tmp.numGames();
     580             :                 //TODO:
     581             :                 //- update bookmarks game number
     582             :                 //  (*it).second   == old game number
     583             :                 //  tmp.numGames() == new game number
     584           0 :                 if (++iProgress % 8192 == 0) {
     585           0 :                         if (!progress.report(iProgress, sort.size())) {
     586           0 :                                 err_UserCancel = true;
     587           0 :                                 break;
     588             :                         }
     589             :                 }
     590             :         }
     591             : 
     592             :         //5) Finalize the new database
     593           0 :         tmp.idx->SetAutoLoad(autoloadNew);
     594           0 :         std::vector<std::string> tmp_filenames = tmp.codec_->getFilenames();
     595           0 :         errorT err_NbWrite = tmp.endTransaction();
     596           0 :         errorT err_Close = tmp.Close();
     597           0 :         if (err_Close == OK) err_Close = (filenames.size() == tmp_filenames.size()) ? OK : ERROR;
     598             : 
     599             :         //6) Error: cleanup and report
     600           0 :         if (err_NbWrite != OK || err_Close != OK || err_UserCancel || err_AddGame != OK) {
     601           0 :                 for (size_t i = 0, n = tmp_filenames.size(); i < n; i++) {
     602           0 :                         std::remove(tmp_filenames[i].c_str());
     603             :                 }
     604           0 :                 if (err_AddGame != OK)
     605           0 :                         return err_AddGame;
     606           0 :                 if (err_UserCancel)
     607           0 :                         return ERROR_UserCancel;
     608           0 :                 if (err_NbWrite != OK)
     609           0 :                         return err_NbWrite;
     610           0 :                 ASSERT(err_Close != OK);
     611           0 :                 return err_Close;
     612             :         }
     613             : 
     614             :         //7) Remember the active filters and SortCaches
     615           0 :         std::vector<std::string> filters(filters_.size());
     616           0 :         for (size_t i = 0, n = filters_.size(); i < n; i++) {
     617           0 :                 filters[i] = filters_[i].first;
     618             :         }
     619           0 :         std::vector< std::pair<std::string, int> > oldSC;
     620           0 :         for (auto& sortCache : sortCaches_) {
     621           0 :                 int refCount = sortCache.second->incrRef(0);
     622           0 :                 if (refCount >= 0)
     623           0 :                         oldSC.emplace_back(sortCache.first, refCount);
     624             :         }
     625             : 
     626             :         //8) Remove the old database
     627           0 :         if (Close() != OK) return ERROR_FileInUse;
     628           0 :         for (size_t i = 0, n = filenames.size(); i < n; i++) {
     629           0 :                 if (std::remove(filenames[i].c_str()) != 0) return ERROR_CompactRemove;
     630             :         }
     631             : 
     632             :         //9) Success: rename the files and open the new database
     633           0 :         for (size_t i = 0, n = filenames.size(); i < n; i++) {
     634           0 :                 const char* s1 = tmp_filenames[i].c_str();
     635           0 :                 const char* s2 = filenames[i].c_str();
     636           0 :                 std::rename(s1, s2);
     637             :         }
     638           0 :         errorT res = Open(dbtype, FMODE_Both, filename.c_str());
     639             : 
     640             :         //10) Re-create filters and SortCaches
     641           0 :         if (res == OK || res == ERROR_NameDataLoss) {
     642           0 :                 for (size_t i = 0, n = filters.size(); i < n; i++) {
     643           0 :                         filters_.push_back(
     644           0 :                             std::make_pair(filters[i], new Filter(numGames())));
     645             :                 }
     646           0 :                 for (size_t i = 0, n = oldSC.size(); i < n; i++) {
     647           0 :                         const std::string& criteria = oldSC[i].first;
     648           0 :                         SortCache* sc = SortCache::create(idx, nb_, criteria.c_str());
     649           0 :                         if (sc != NULL) {
     650           0 :                                 sc->incrRef(oldSC[i].second);
     651           0 :                                 sortCaches_.emplace_back(criteria, sc);
     652             :                         }
     653             :                 }
     654             :         }
     655             : 
     656           0 :         return res;
     657             : }
     658             : 
     659             : /**
     660             :  * Retrieve a SortCache object matching the supplied @e criteria.
     661             :  * A new SortCache with refCount equal to 0 is created if a suitable object is
     662             :  * not found in @e sortCaches_. Objects with refCount <= 0 are destroyed by the
     663             :  * @e releaseSortCache function independently from the provided @e criteria
     664             :  * argument (implementing a rudimentary garbage collector).
     665             :  * @param criteria: the list of fields by which games will be ordered.
     666             :  *                  Each field should be followed by '+' to indicate an
     667             :  *                  ascending order or by '-' for a descending order.
     668             :  * @returns a pointer to a SortCache object in case of success, NULL otherwise.
     669             :  */
     670       19817 : SortCache* scidBaseT::getSortCache(const char* criteria) {
     671       19817 :         ASSERT(criteria != NULL);
     672             : 
     673      691102 :         for (auto& sortCache : sortCaches_) {
     674      675969 :                 if (sortCache.first == criteria)
     675        4684 :                         return sortCache.second;
     676             :         }
     677             : 
     678       15133 :         SortCache* sc = SortCache::create(idx, getNameBase(), criteria);
     679       15133 :         if (sc != NULL)
     680          76 :                 sortCaches_.emplace_back(criteria, sc);
     681             : 
     682       15133 :         return sc;
     683             : }
     684             : 
     685         795 : void scidBaseT::releaseSortCache(const char* criteria) {
     686         795 :         size_t i = 0;
     687       29753 :         while (i < sortCaches_.size()) {
     688       14479 :                 const char* tmp = sortCaches_[i].first.c_str();
     689       14479 :                 int decr = std::strcmp(criteria, tmp) ? 0 : -1;
     690       14553 :                 if (sortCaches_[i].second->incrRef(decr) <= 0) {
     691          74 :                         delete sortCaches_[i].second;
     692          74 :                         sortCaches_.erase(sortCaches_.begin() + i);
     693          74 :                         continue; //do not increment i
     694             :                 }
     695       14405 :                 i += 1;
     696             :         }
     697         795 : }
     698             : 
     699         797 : SortCache* scidBaseT::createSortCache(const char* criteria) {
     700         797 :         SortCache* sc = getSortCache(criteria);
     701         797 :         if (sc != NULL)
     702          80 :                 sc->incrRef(1);
     703             : 
     704         797 :         return sc;
     705             : }
     706             : 
     707       15900 : size_t scidBaseT::listGames(const char* criteria, size_t start, size_t count,
     708             :                             const HFilter& filter, gamenumT* destCont) {
     709       15900 :         const SortCache* sc = getSortCache(criteria);
     710       15900 :         if (sc == NULL)
     711       14340 :                 return 0;
     712             : 
     713        1560 :         return sc->select(start, count, filter, destCont);
     714             : }
     715             : 
     716        3120 : size_t scidBaseT::sortedPosition(const char* criteria, const HFilter& filter,
     717             :                                  gamenumT gameId) {
     718        3120 :         ASSERT(filter != NULL && filter->size() <= numGames());
     719             : 
     720        3120 :         if (gameId >= numGames() || filter->get(gameId) == 0)
     721           0 :                 return INVALID_GAMEID;
     722             : 
     723        3120 :         SortCache* sc = getSortCache(criteria);
     724        3120 :         if (sc == NULL)
     725           0 :                 return INVALID_GAMEID;
     726             : 
     727        3120 :         return sc->sortedPosition(gameId, filter);
     728             : }

Generated by: LCOV version 1.13