LCOV - code coverage report
Current view: top level - src - scidbase.cpp (source / functions) Hit Total Coverage
Test: test_coverage.info Lines: 277 510 54.3 %
Date: 2017-06-21 14:32:49 Functions: 27 37 73.0 %

          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          47 : ICodecDatabase* ICodecDatabase::make(Codec codec, errorT* resError,
      30             :                                      fileModeT fMode, const char* filename,
      31             :                                      const Progress& progress, Index* idx,
      32             :                                      NameBase* nb) {
      33          47 :         ICodecDatabase* res = 0;
      34          47 :         errorT err = ERROR;
      35          47 :         switch (codec) {
      36             :         case ICodecDatabase::MEMORY:
      37          16 :                 res = new CodecMemory();
      38             :                 break;
      39             :         case ICodecDatabase::SCID4:
      40          18 :                 res = new CodecScid4();
      41             :                 break;
      42             :         case ICodecDatabase::PGN:
      43          13 :                 res = new CodecPgn();
      44             :                 break;
      45             :         }
      46             : 
      47          47 :         if (res != 0) {
      48          47 :                 err = res->dyn_open(fMode, filename, progress, idx, nb);
      49          47 :                 if (err != OK && err != ERROR_NameDataLoss) {
      50           9 :                         delete res;
      51           9 :                         res = 0;
      52             :                 }
      53             :         }
      54          47 :         if (resError) *resError = err;
      55          47 :         return res;
      56             : }
      57             : 
      58          80 : scidBaseT::scidBaseT() {
      59           8 :         idx = new Index;
      60           8 :         nb = new NameBase;
      61           8 :         game = new Game;
      62           8 :         gameNumber = -1;
      63           8 :         gameAltered = false;
      64           8 :         inUse = false;
      65           8 :         tree.moveCount = tree.totalCount = 0;
      66           8 :         fileMode_ = FMODE_None;
      67           8 :         codec_ = 0;
      68          16 :         bbuf = new ByteBuffer(BBUF_SIZE);
      69          16 :         dbFilter = new Filter(0);
      70          16 :         treeFilter = new Filter(0);
      71           8 :         duplicates_ = NULL;
      72           8 :         stats_ = NULL;
      73           8 : }
      74             : 
      75          32 : scidBaseT::~scidBaseT() {
      76           8 :         if (inUse)
      77           8 :                 Close();
      78             : 
      79           8 :         delete[] duplicates_;
      80          16 :         delete idx;
      81           8 :         delete nb;
      82           8 :         delete game;
      83          16 :         delete bbuf;
      84           8 :         delete stats_;
      85          16 :         delete dbFilter;
      86          16 :         delete treeFilter;
      87           8 : }
      88             : 
      89           8 : errorT scidBaseT::Open(ICodecDatabase::Codec dbtype, fileModeT fMode,
      90             :                        const char* filename, const Progress& progress) {
      91           8 :         if (inUse) return ERROR_FileInUse;
      92           8 :         if (filename == 0) filename = "";
      93             : 
      94           8 :         inUse = true;
      95             : 
      96           8 :         delete codec_;
      97           8 :         errorT err = OK;
      98           8 :         codec_ = ICodecDatabase::make(dbtype, &err, fMode, filename, progress, idx, nb);
      99           8 :         if (codec_ == 0) {
     100           0 :                 idx->Close();
     101           0 :                 nb->Clear();
     102           0 :                 inUse = false;
     103           0 :                 return err;
     104             :         }
     105             : 
     106           8 :         if (fMode == FMODE_Create) fMode = FMODE_Both;
     107           8 :         fileMode_ = fMode;
     108          16 :         fileName_ = filename;
     109           8 :         gameNumber = -1;
     110             : 
     111             :         // Initialize the filters: all the games are included by default.
     112          24 :         dbFilter->Init(numGames());
     113          24 :         treeFilter->Init(numGames());
     114          16 :         ASSERT(filters_.empty());
     115             : 
     116             :         // Ensure an old treefile is not still around:
     117          24 :         std::remove((fileName_ + ".stc").c_str());
     118             :         // Default treeCache size: 250
     119           8 :         treeCache.CacheResize(250);
     120             : 
     121           8 :         return err;
     122             : }
     123             : 
     124           8 : errorT scidBaseT::Close () {
     125           8 :         ASSERT(inUse);
     126             : 
     127          18 :         for (size_t i = 0, n = sortCaches_.size(); i < n; i++) {
     128           4 :                 delete sortCaches_[i].second;
     129             :         }
     130           8 :         sortCaches_.resize(0);
     131             : 
     132           8 :         errorT errGFile = codec_->flush();
     133          16 :         errorT errIdx = idx->Close();
     134           8 :         nb->Clear();
     135           8 :         delete codec_;
     136             : 
     137           8 :         codec_ = NULL;
     138           8 :         clear();
     139           8 :         game->Clear();
     140           8 :         fileMode_ = FMODE_None;
     141          16 :         fileName_ = "<empty>";
     142           8 :         gameNumber = -1;
     143           8 :         gameAltered = false;
     144          16 :         dbFilter->Init(0);
     145          16 :         treeFilter->Init(0);
     146         170 :         for (size_t i=0, n = filters_.size(); i < n; i++) delete filters_[i].second;
     147           8 :         filters_.clear();
     148           8 :         inUse = false;
     149             : 
     150           8 :         return (errIdx != OK) ? errIdx : errGFile;
     151             : }
     152             : 
     153             : 
     154          19 : void scidBaseT::clear() {
     155          19 :         if (stats_ != NULL) { delete stats_; stats_ = NULL;}
     156          19 :         if (duplicates_ != NULL) { delete[] duplicates_; duplicates_ = NULL; }
     157          19 :         treeCache.Clear();
     158          95 :         for (nameT nt = NAME_PLAYER; nt < NUM_NAME_TYPES; nt++) {
     159          76 :                 nameFreq_[nt].resize(0);
     160             :         }
     161          19 : }
     162             : 
     163          11 : void scidBaseT::beginTransaction() {
     164          22 :         for (size_t i = 0, n = sortCaches_.size(); i < n; ++i) {
     165           0 :                 sortCaches_[i].second->prepareForChanges();
     166             :         }
     167          11 : }
     168             : 
     169          11 : errorT scidBaseT::endTransaction(gamenumT gNum) {
     170          11 :         clear();
     171          11 :         errorT res = codec_->flush();
     172             : 
     173          22 :         for (size_t i = 0, n = sortCaches_.size(); i < n; ++i) {
     174           0 :                 sortCaches_[i].second->checkForChanges(gNum);
     175             :         }
     176             : 
     177          11 :         return res;
     178             : }
     179             : 
     180           0 : errorT scidBaseT::getExtraInfo(const std::string& tagname, std::string* res) const {
     181           0 :         if (tagname == "description") {
     182           0 :                 *res = idx->GetDescription();
     183           0 :         } else if (tagname == "autoload") {
     184           0 :                 *res = to_string(idx->GetAutoLoad());
     185           0 :         } else if (tagname == "type") {
     186           0 :                 *res = to_string(idx->GetType());
     187           0 :         } else if (tagname.length() == 5 && tagname.find("flag") == 0) {
     188           0 :                 uint flagType = IndexEntry::CharToFlag(tagname[4]);
     189           0 :                 if (flagType == 0) return ERROR_BadArg;
     190           0 :                 const char* desc = idx->GetCustomFlagDesc(flagType);;
     191           0 :                 if (desc == 0) return ERROR_BadArg;
     192             :                 *res = desc;
     193             :         } else {
     194             :                 return ERROR_BadArg;
     195             :         }
     196             :         return OK;
     197             : }
     198             : 
     199           0 : errorT scidBaseT::setExtraInfo(const std::string& tagname, const char* new_value) {
     200           0 :         if (tagname == "description") {
     201           0 :                 return idx->SetDescription(new_value);
     202           0 :         } else if (tagname == "autoload") {
     203           0 :                 return idx->SetAutoLoad(strGetUnsigned(new_value));
     204           0 :         } else if (tagname == "type") {
     205           0 :                 return idx->SetType(strGetUnsigned(new_value));
     206           0 :         } else if (tagname.length() == 5 && tagname.find("flag") == 0) {
     207           0 :                 uint flagType = IndexEntry::CharToFlag(tagname[4]);
     208           0 :                 if (flagType == 0) return ERROR_BadArg;
     209           0 :                 if (idx->GetCustomFlagDesc(flagType) == 0) return ERROR_BadArg;
     210           0 :                 return idx->SetCustomFlagDesc(flagType, new_value);
     211             :         }
     212             :         return ERROR_BadArg;
     213             : }
     214             : 
     215             : /**
     216             : * scidBaseT::makeGamePos() - constructs a GamePos object
     217             : * @game: a Game object with a valid current position
     218             : * @ravNum: current variation number
     219             : *
     220             : * This function extracts informations from the current position of Game @game
     221             : * and create a GamePos object with the corresponding informations:
     222             : * RAVdepth: current variation depth.
     223             : * RAVnum: current variation num.
     224             : * FEN: "Forsyth-Edwards Notation" describing the current position.
     225             : * NAGS: "Numeric Annotation Glyph" is a non-negative integer from 0 to 255
     226             : *       used to indicate a simple annotation in a language independent manner.
     227             : * comment: text annotation of the current position.
     228             : * lastMoveSAN: the last move that was played to reach the current position.
     229             : *              The move is indicated using English "Standard Algebraic Notation".
     230             : */
     231        1124 : scidBaseT::GamePos scidBaseT::makeGamePos(Game& game, unsigned int ravNum) {
     232        1124 :         GamePos res;
     233        1124 :         res.RAVdepth = game.GetVarLevel();
     234        1124 :         res.RAVnum = ravNum;
     235             :         char strBuf[256];
     236        1124 :         game.GetCurrentPos()->PrintFEN(strBuf, FEN_ALL_FIELDS);
     237        2248 :         res.FEN = strBuf;
     238        2472 :         for (byte* nag = game.GetNags(); *nag; nag++) {
     239         448 :                 res.NAGs.push_back(*nag);
     240             :         }
     241        3372 :         res.comment = game.GetMoveComment();
     242        1124 :         game.GetPrevSAN(strBuf);
     243        2248 :         res.lastMoveSAN = strBuf;
     244        1124 :         return res;
     245             : }
     246             : 
     247             : /**
     248             : * scidBaseT::getGame() - returns all the positions of a game
     249             : * @ie: a valid pointer to the IndexEntry of the desired game
     250             : * @dest: a container of GamePos objects where the positions will be stored.
     251             : *
     252             : * This function iterate all the positions of the game pointed by @ie and
     253             : * stores the positions in @dest. The container is NOT automatically cleared
     254             : * and the container should support push_back().
     255             : * The order of positions and of Recursive Annotation Variations (RAV)
     256             : * follows the pgn standard: "The alternate move sequence given by an RAV is
     257             : * one that may be legally played by first unplaying the move that appears
     258             : * immediately prior to the RAV. Because the RAV is a recursive construct,
     259             : * it may be nested."
     260             : * Each position have a RAVdepth and a RAVnum that allows to follow a
     261             : * variation from any given position:
     262             : * - skip all the next positions with a bigger RAVdepth
     263             : * - the variation ends with:
     264             : *   - a lower RAVdepth or
     265             : *   - an equal RAVdepth but different RAVnum or
     266             : *   - the end of @dest
     267             : *
     268             : * Return OK if successful.
     269             : */
     270          12 : errorT scidBaseT::getGame(const IndexEntry* ie, std::vector<GamePos>& dest) {
     271          12 :         ASSERT(ie != 0);
     272             : 
     273             :         // Create the Game object
     274          24 :         ByteBuffer buf(BBUF_SIZE);
     275          12 :         if (getGame(ie, &buf) != OK) {
     276             :                 return ERROR_Decode;
     277             :         }
     278          24 :         Game game;
     279          12 :         if (game.Decode(&buf, GAME_DECODE_ALL) != OK) {
     280             :                 return ERROR_Decode;
     281             :         }
     282          12 :         std::vector<int> endPos = game.GetCurrentLocation();
     283          12 :         game.MoveToPly(0);
     284             : 
     285             :         // Add start FEN
     286          24 :         dest.push_back(makeGamePos(game, 0));
     287             : 
     288             :         // Iterate all the positions of the game
     289          24 :         std::vector< std::pair<uint, uint> > rav;
     290          36 :         rav.push_back(std::make_pair(0, 0));
     291          12 :         errorT err = OK;
     292        1270 :         while (err == OK) {
     293        2540 :                 uint nVariations = game.GetNumVariations();
     294        1270 :                 err = game.MoveForward();
     295        1270 :                 if (err == OK) {
     296        2224 :                         dest.push_back(makeGamePos(game, rav.back().first));
     297             :                 }
     298             : 
     299        1270 :                 if (nVariations != 0) {
     300         134 :                         if (err == OK) {
     301             :                                 // Go back in order to process variations
     302         134 :                                 err = game.MoveBackup();
     303         134 :                                 if (err != OK) break;
     304             :                         }
     305             :                         // Enter the first variation
     306         134 :                         err = game.MoveIntoVariation(0);
     307         536 :                         rav.push_back(std::make_pair(0, nVariations));
     308             :                 } else {
     309        1136 :                         if (err == ERROR_EndOfMoveList) {
     310             :                                 // Leave the current variation
     311         158 :                                 err = game.MoveExitVariation();
     312         158 :                                 if (err != OK) break;
     313             : 
     314         292 :                                 if (++rav.back().first < rav.back().second) {
     315             :                                         // Enter the next variation
     316          12 :                                         err = game.MoveIntoVariation(rav.back().first);
     317             :                                 } else {
     318             :                                         // All the sub-variation has been processed
     319         134 :                                         rav.pop_back();
     320             :                                         // Skip the main move of the parent variation
     321         134 :                                         err = game.MoveForward();
     322             :                                 }
     323             :                         }
     324             :                 }
     325             :         }
     326          36 :         if (rav.size() == 1 && game.GetCurrentLocation() == endPos) return OK;
     327           0 :         return err;
     328             : }
     329             : 
     330           9 : errorT scidBaseT::saveGame(Game* game, gamenumT replacedGameId) {
     331           9 :         beginTransaction();
     332           9 :         errorT err1 = saveGameHelper(game, replacedGameId);
     333           9 :         errorT err2 = endTransaction(replacedGameId);
     334           9 :         return (err1 != OK) ? err1 : err2;
     335             : }
     336             : 
     337           9 : errorT scidBaseT::saveGameHelper(Game* game, gamenumT gameId) {
     338           9 :         if (isReadOnly())
     339             :                 return ERROR_FileReadOnly;
     340             : 
     341          18 :         if (gameId < numGames())
     342           1 :                 return codec_->saveGame(game, gameId);
     343             : 
     344           8 :         errorT err = codec_->addGame(game);
     345           8 :         if (err == OK)
     346           8 :                 extendFilters();
     347             :         return err;
     348             : }
     349             : 
     350           3 : errorT scidBaseT::importGame(const scidBaseT* srcBase, uint gNum) {
     351           3 :         if (srcBase == this) return ERROR_BadArg;
     352           2 :         if (isReadOnly()) return ERROR_FileReadOnly;
     353           4 :         if (gNum >= srcBase->numGames()) return ERROR_BadArg;
     354             : 
     355           1 :         beginTransaction();
     356           1 :         errorT err = importGameHelper(srcBase, gNum);
     357           1 :         errorT errClear = endTransaction();
     358           1 :         return (err == OK) ? errClear : err;
     359             : }
     360             : 
     361           2 : errorT scidBaseT::importGames(const scidBaseT* srcBase, const HFilter& filter, const Progress& progress) {
     362           2 :         ASSERT(srcBase != 0);
     363           2 :         ASSERT(filter != 0);
     364           2 :         if (srcBase == this) return ERROR_BadArg;
     365           1 :         if (isReadOnly()) return ERROR_FileReadOnly;
     366             : 
     367           1 :         beginTransaction();
     368           1 :         errorT err = OK;
     369           1 :         size_t iProgress = 0;
     370           1 :         size_t totGames = filter->size();
     371           5 :         for (gamenumT gNum = 0, n = srcBase->numGames(); gNum < n; gNum++) {
     372           3 :                 if (filter.get(gNum) == 0) continue;
     373           2 :                 err = importGameHelper(srcBase, gNum);
     374           2 :                 if (err != OK) break;
     375           2 :                 if (++iProgress % 8192 == 0) {
     376           0 :                         if (!progress.report(iProgress, totGames)) break;
     377             :                 }
     378             :         }
     379           1 :         errorT errClear = endTransaction();
     380           1 :         return (err == OK) ? errClear : err;
     381             : }
     382             : 
     383           3 : errorT scidBaseT::importGameHelper(const scidBaseT* sourceBase, uint gNum) {
     384           6 :         const IndexEntry* srcIe = sourceBase->getIndexEntry(gNum);
     385           6 :         uint gameDataLen = srcIe->GetLength();
     386             :         const byte* gameData =
     387           3 :             sourceBase->codec_->getGameData(srcIe->GetOffset(), gameDataLen);
     388           3 :         if (gameData == 0) return ERROR_FileRead;
     389             : 
     390           3 :         IndexEntry ie = *srcIe;
     391             :         errorT err;
     392           6 :         err = ie.SetWhiteName(nb, srcIe->GetWhiteName(sourceBase->nb));
     393           3 :         if (err != OK) return err;
     394           6 :         err = ie.SetBlackName(nb, srcIe->GetBlackName(sourceBase->nb));
     395           3 :         if (err != OK) return err;
     396           6 :         err = ie.SetEventName(nb, srcIe->GetEventName(sourceBase->nb));
     397           3 :         if (err != OK) return err;
     398           6 :         err = ie.SetSiteName(nb, srcIe->GetSiteName(sourceBase->nb));
     399           3 :         if (err != OK) return err;
     400           6 :         err = ie.SetRoundName(nb, srcIe->GetRoundName(sourceBase->nb));
     401           3 :         if (err != OK) return err;
     402             : 
     403          12 :         nb->AddElo(ie.GetWhite(), ie.GetWhiteElo());
     404          12 :         nb->AddElo(ie.GetBlack(), ie.GetBlackElo());
     405             : 
     406           3 :         err = codec_->addGame(&ie, gameData, gameDataLen);
     407           3 :         if (err == OK) extendFilters();
     408             :         return err;
     409             : }
     410             : 
     411             : /**
     412             :  * Filters
     413             :  */
     414         102 : std::string scidBaseT::newFilter() {
     415         204 :         std::string newname = (filters_.size() == 0)
     416             :                 ? "a_"
     417         204 :                 : filters_.back().first;
     418         102 :         if (newname[0] == 'z') {
     419           6 :                 newname = 'a' + newname;
     420             :         } else {
     421         396 :                 newname = ++(newname[0]) + newname.substr(1);
     422             :         }
     423         612 :         filters_.push_back(std::make_pair(newname, new Filter(numGames())));
     424         102 :         return newname;
     425             : }
     426             : 
     427       17395 : std::string scidBaseT::composeFilter(const std::string& mainFilter,
     428             :                                      const std::string& maskFilter) const {
     429       17395 :         std::string res;
     430       17395 :         if (mainFilter.empty()) return res;
     431             : 
     432       17265 :         if (mainFilter[0] != '+') {
     433             :                 res = mainFilter;
     434             :         } else {
     435        8795 :                 size_t maskName = mainFilter.find('+', 1);
     436        8795 :                 if (maskName != std::string::npos)
     437       16810 :                         res = mainFilter.substr(1, maskName - 1);
     438             :         }
     439             : 
     440       17265 :         if (!maskFilter.empty()) {
     441       36480 :                 res = '+' + res + "+" + maskFilter;
     442             :         }
     443             : 
     444       17265 :         if (getFilter(res) == 0) res.clear();
     445             :         return res;
     446             : }
     447             : 
     448          29 : void scidBaseT::deleteFilter(const char* filterId) {
     449        1433 :         for (size_t i = 0, n = filters_.size(); i < n; i++) {
     450        4200 :                 if (filters_[i].first == filterId) {
     451          75 :                         delete filters_[i].second;
     452         125 :                         filters_.erase(filters_.begin() + i);
     453          25 :                         break;
     454             :                 }
     455             :         }
     456          29 : }
     457             : 
     458          11 : void scidBaseT::extendFilters() {
     459          11 :         dbFilter->Append(dbFilter->isWhole() ? 1 : 0);
     460          11 :         treeFilter->Append(treeFilter->isWhole() ? 1 : 0);
     461          22 :         for (size_t i = 0, n = filters_.size(); i < n; i++) {
     462           0 :                 Filter* filter = filters_[i].second;
     463           0 :                 filter->Append(filter->isWhole() ? 1 : 0);
     464             :         }
     465          11 : }
     466             : 
     467       53686 : Filter* scidBaseT::fetchFilter(const std::string& filterId) const {
     468       53686 :         if (filterId == "dbfilter") return dbFilter;
     469       53162 :         if (filterId == "tree") return treeFilter;
     470             : 
     471     2135582 :         for (size_t i = 0, n = filters_.size(); i < n; i++) {
     472     4156906 :                 if (filterId == filters_[i].first)
     473       45819 :                         return filters_[i].second;
     474             :         }
     475             :         return 0;
     476             : }
     477             : 
     478       36551 : HFilter scidBaseT::getFilterHelper(const std::string& filterId,
     479             :                                    bool unmasked) const {
     480       36551 :         Filter* main = 0;
     481       36551 :         const Filter* mask = 0;
     482       71217 :         if (filterId.empty() || filterId[0] != '+') {
     483       18636 :                 main = fetchFilter(filterId);
     484             :         } else {
     485       17915 :                 size_t maskName = filterId.find('+', 1);
     486       17915 :                 if (maskName != std::string::npos) {
     487       35050 :                         main = fetchFilter(filterId.substr(1, maskName - 1));
     488       35050 :                         if (!unmasked) mask = fetchFilter(filterId.substr(maskName + 1));
     489             :                 }
     490             :         }
     491       36551 :         return HFilter(main, mask);
     492             : }
     493             : 
     494             : /**
     495             :  * Statistics
     496             :  */
     497           0 : const scidBaseT::Stats& scidBaseT::getStats() const {
     498           0 :         if (stats_ == NULL) stats_ = new scidBaseT::Stats(this);
     499           0 :         return *stats_;
     500             : }
     501             : 
     502           0 : scidBaseT::Stats::Eco::Eco()
     503           0 : : count(0) {
     504           0 :         std::fill_n(results, NUM_RESULT_TYPES, 0);
     505           0 : }
     506             : 
     507           0 : scidBaseT::Stats::Stats(const scidBaseT* dbase) {
     508           0 :         std::fill(flagCount, flagCount + IndexEntry::IDX_NUM_FLAGS, 0);
     509           0 :         minDate = ZERO_DATE;
     510           0 :         maxDate = ZERO_DATE;
     511           0 :         nYears = 0;
     512           0 :         sumYears = 0;
     513           0 :         std::fill_n(nResults, NUM_RESULT_TYPES, 0);
     514           0 :         nRatings = 0;
     515           0 :         sumRatings = 0;
     516           0 :         minRating = 0;
     517           0 :         maxRating = 0;
     518             : 
     519             :         // Read stats from index entry of each game:
     520           0 :         for (gamenumT gnum=0, n = dbase->numGames(); gnum < n; gnum++) {
     521           0 :                 const IndexEntry* ie = dbase->getIndexEntry(gnum);
     522           0 :                 nResults[ie->GetResult()]++;
     523           0 :                 eloT elo = ie->GetWhiteElo();
     524           0 :                 if (elo > 0) {
     525           0 :                         nRatings++;
     526           0 :                         sumRatings += elo;
     527           0 :                         if (minRating == 0) { minRating = elo; }
     528           0 :                         if (elo < minRating) { minRating = elo; }
     529           0 :                         if (elo > maxRating) { maxRating = elo; }
     530             :                 }
     531           0 :                 elo = ie->GetBlackElo();
     532           0 :                 if (elo > 0) {
     533           0 :                         nRatings++;
     534           0 :                         sumRatings += elo;
     535           0 :                         if (minRating == 0) { minRating = elo; }
     536           0 :                         if (elo < minRating) { minRating = elo; }
     537           0 :                         if (elo > maxRating) { maxRating = elo; }
     538             :                 }
     539           0 :                 dateT date = ie->GetDate();
     540           0 :                 if (gnum == 0) {
     541           0 :                         maxDate = minDate = date;
     542             :                 }
     543           0 :                 if (date_GetYear(date) > 0) {
     544           0 :                         if (date < minDate) { minDate = date; }
     545           0 :                         if (date > maxDate) { maxDate = date; }
     546           0 :                         nYears++;
     547           0 :                         sumYears += date_GetYear (date);
     548             :                 }
     549             : 
     550           0 :                 for (uint flag = 0; flag < IndexEntry::IDX_NUM_FLAGS; flag++) {
     551           0 :                         bool value = ie->GetFlag (1 << flag);
     552           0 :                         if (value) {
     553           0 :                                 flagCount[flag]++;
     554             :                         }
     555             :                 }
     556             : 
     557           0 :                 resultT result = ie->GetResult();
     558           0 :                 ecoT eco = ie->GetEcoCode();
     559           0 :                 if (eco == 0) {
     560           0 :                         ecoEmpty_.count++;
     561           0 :                         ecoEmpty_.results[result]++;
     562             :                 } else {
     563           0 :                         ecoValid_.count++;
     564           0 :                         ecoValid_.results[result]++;
     565           0 :                         eco = eco_Reduce(eco);
     566           0 :                         ecoStats_[eco].count++;
     567           0 :                         ecoStats_[eco].results[result]++;
     568           0 :                         eco /= 27;
     569           0 :                         ecoGroup3_[eco].count++;
     570           0 :                         ecoGroup3_[eco].results[result]++;
     571           0 :                         eco /= 10;
     572           0 :                         ecoGroup2_[eco].count++;
     573           0 :                         ecoGroup2_[eco].results[result]++;
     574           0 :                         eco /= 10;
     575           0 :                         ecoGroup1_[eco].count++;
     576           0 :                         ecoGroup1_[eco].results[result]++;
     577             :                 }
     578             :         }
     579           0 : }
     580             : 
     581           0 : const scidBaseT::Stats::Eco* scidBaseT::Stats::getEcoStats(const char* ecoStr) const {
     582           0 :         ASSERT(ecoStr != 0);
     583             : 
     584           0 :         if (*ecoStr == 0) return &ecoValid_;
     585             : 
     586           0 :         ecoT eco = eco_FromString(ecoStr);
     587           0 :         if (eco == 0) return 0;
     588           0 :         eco = eco_Reduce(eco);
     589             : 
     590           0 :         switch(strlen(ecoStr)) {
     591             :         case 0:
     592           0 :                 return &ecoValid_;
     593             :         case 1:
     594           0 :                 return &(ecoGroup1_[eco / 2700]);
     595             :         case 2:
     596           0 :                 return &(ecoGroup2_[eco / 270]);
     597             :         case 3:
     598           0 :                 return &(ecoGroup3_[eco / 27]);
     599             :         case 4:
     600             :         case 5:
     601           0 :                 return &(ecoStats_[eco]);
     602             :         }
     603             : 
     604             :         return 0;
     605             : }
     606             : 
     607             : 
     608             : 
     609             : double scidBaseT::TreeStat::expVect_[1600];
     610             : 
     611           0 : scidBaseT::TreeStat::TreeStat()
     612           0 : : toMove(NOCOLOR), resultW(0), resultD(0), resultB(0), exp(0), ngames(0), nexp(0)
     613             : {
     614           0 :         if (TreeStat::expVect_[0] == 0) {
     615           0 :                 for (int i=-800; i < 800; i++) TreeStat::expVect_[i+800] = 1/(1 + pow(10, i/400.0));
     616             :         }
     617           0 : }
     618             : 
     619           0 : std::vector<scidBaseT::TreeStat> scidBaseT::getTreeStat(const HFilter& filter) {
     620           0 :         ASSERT(filter != 0);
     621             : 
     622           0 :         std::vector<scidBaseT::TreeStat> res;
     623           0 :         std::vector<FullMove> v;
     624           0 :         for (gamenumT gnum = 0, n = numGames(); gnum < n; gnum++) {
     625           0 :                 uint ply = filter.get(gnum);
     626           0 :                 if (ply == 0) continue;
     627           0 :                 else ply--;
     628             : 
     629           0 :                 const IndexEntry* ie = getIndexEntry(gnum);
     630           0 :                 FullMove move = StoredLine::getMove(ie->GetStoredLineCode(), ply);
     631           0 :                 if (move.isNull()) {
     632           0 :                         move = getGame(ie).getMove(ply);
     633             :                 }
     634             : 
     635             :                 size_t i = 0;
     636           0 :                 while (i < v.size() && v[i] != move) i++;
     637           0 :                 if (i == v.size()) {
     638           0 :                         v.push_back(move);
     639           0 :                         res.push_back(scidBaseT::TreeStat());
     640             :                 }
     641           0 :                 res[i].add(ie->GetResult(), ie->GetWhiteElo(nb), ie->GetBlackElo(nb));
     642             :         }
     643             : 
     644           0 :         for (size_t i = 0, n = v.size(); i < n; i++) {
     645           0 :                 res[i].SAN = (v[i].isNull()) ? "[end]" : v[i].getSAN(&(res[i].toMove));
     646             :         }
     647             : 
     648           0 :         std::sort(res.begin(), res.end());
     649           0 :         return res;
     650             : }
     651             : 
     652           0 : errorT scidBaseT::getCompactStat(uint* n_deleted,
     653             :                                  uint* n_unused,
     654             :                                  uint* n_sparse,
     655             :                                  uint* n_badNameId) {
     656           0 :         std::vector<uint> nbFreq[NUM_NAME_TYPES];
     657           0 :         for (nameT n = NAME_PLAYER; n < NUM_NAME_TYPES; n++) {
     658           0 :                 nbFreq[n].resize(nb->GetNumNames(n), 0);
     659             :         }
     660             : 
     661           0 :         uint last_offset = 0;
     662           0 :         *n_sparse = 0;
     663           0 :         *n_deleted = 0;
     664           0 :         for (gamenumT i=0, n = numGames(); i < n; i++) {
     665           0 :                 const IndexEntry* ie = getIndexEntry (i);
     666           0 :                 if (ie->GetDeleteFlag()) { *n_deleted += 1; continue; }
     667             : 
     668           0 :                 uint offset = ie->GetOffset();
     669           0 :                 if (offset < last_offset) *n_sparse += 1;
     670           0 :                 last_offset = offset;
     671             : 
     672           0 :                 nbFreq[NAME_PLAYER][ie->GetWhite()] += 1;
     673           0 :                 nbFreq[NAME_PLAYER][ie->GetBlack()] += 1;
     674           0 :                 nbFreq[NAME_EVENT][ie->GetEvent()] += 1;
     675           0 :                 nbFreq[NAME_SITE][ie->GetSite()] += 1;
     676           0 :                 nbFreq[NAME_ROUND][ie->GetRound()] += 1;
     677             :         }
     678             : 
     679           0 :         *n_unused = 0;
     680           0 :         for (nameT n = NAME_PLAYER; n < NUM_NAME_TYPES; n++) {
     681           0 :                 *n_unused += std::count(nbFreq[n].begin(), nbFreq[n].end(), 0);
     682             :         }
     683             : 
     684           0 :         *n_badNameId = idx->GetBadNameIdCount();
     685           0 :         return OK;
     686             : }
     687             : 
     688           0 : errorT scidBaseT::compact(const Progress& progress) {
     689           0 :         std::vector<std::string> filenames = codec_->getFilenames();
     690           0 :         if (filenames.empty()) return ERROR_CodecUnsupFeat;
     691             : 
     692           0 :         if (fileMode_ != FMODE_Both) {
     693             :                 //Older scid version to be upgraded are opened read only
     694           0 :                 if (idx->GetVersion() == SCID_VERSION) return ERROR_FileMode;
     695             :         }
     696             : 
     697             :         //1) Create a new temporary database
     698           0 :         std::string filename = fileName_;
     699           0 :         std::string tmpfile = filename + "__COMPACT__";
     700           0 :         ICodecDatabase::Codec dbtype = codec_->getType();
     701           0 :         scidBaseT tmp;
     702           0 :         errorT err_Create = tmp.Open(dbtype, FMODE_Create, tmpfile.c_str());
     703           0 :         if (err_Create != OK) return err_Create;
     704             : 
     705             :         //2) Copy the Index Header
     706           0 :         tmp.beginTransaction();
     707           0 :         tmp.idx->copyHeaderInfo(*idx);
     708           0 :         gamenumT autoloadOld = idx->GetAutoLoad();
     709           0 :         gamenumT autoloadNew = 1;
     710             : 
     711             :         //3) Create the list of games to be copied
     712             :         typedef std::vector< std::pair<byte, uint> > sort_t;
     713           0 :         sort_t sort;
     714           0 :         uint n_deleted = 0;
     715           0 :         for (gamenumT i = 0, n = numGames(); i < n; i++) {
     716           0 :                 const IndexEntry* ie = getIndexEntry(i);
     717           0 :                 if (ie->GetDeleteFlag()) {
     718             :                         n_deleted++;
     719             :                         continue;
     720             :                 }
     721           0 :                 byte stLine = ie->GetStoredLineCode();
     722           0 :                 sort.push_back(std::make_pair(stLine, i));
     723             :         }
     724           0 :         std::stable_sort(sort.begin(), sort.end());
     725             : 
     726             :         //4) Copy the games
     727           0 :         uint iProgress = 0;
     728           0 :         bool err_UserCancel = false;
     729           0 :         errorT err_AddGame = OK;
     730           0 :         for (sort_t::iterator it = sort.begin(); it != sort.end(); it++) {
     731           0 :                 err_AddGame = tmp.importGameHelper(this, (*it).second);
     732           0 :                 if (err_AddGame != OK) break;
     733             : 
     734           0 :                 gamenumT oldGnum = it->second + 1;
     735           0 :                 if (oldGnum == autoloadOld) autoloadNew = tmp.numGames();
     736             :                 //TODO:
     737             :                 //- update bookmarks game number
     738             :                 //  (*it).second   == old game number
     739             :                 //  tmp.numGames() == new game number
     740           0 :                 if (++iProgress % 8192 == 0) {
     741           0 :                         if (!progress.report(iProgress, sort.size())) {
     742             :                                 err_UserCancel = true;
     743             :                                 break;
     744             :                         }
     745             :                 }
     746             :         }
     747             : 
     748             :         //5) Finalize the new database
     749           0 :         tmp.idx->SetAutoLoad(autoloadNew);
     750           0 :         std::vector<std::string> tmp_filenames = tmp.codec_->getFilenames();
     751           0 :         errorT err_NbWrite = tmp.endTransaction();
     752           0 :         errorT err_Close = tmp.Close();
     753           0 :         if (err_Close == OK) err_Close = (filenames.size() == tmp_filenames.size()) ? OK : ERROR;
     754             : 
     755             :         //6) Error: cleanup and report
     756           0 :         if (err_NbWrite != OK || err_Close != OK || err_UserCancel || err_AddGame != OK) {
     757           0 :                 for (size_t i = 0, n = tmp_filenames.size(); i < n; i++) {
     758           0 :                         std::remove(tmp_filenames[i].c_str());
     759             :                 }
     760           0 :                 if (err_AddGame != OK)
     761             :                         return err_AddGame;
     762           0 :                 if (err_UserCancel)
     763             :                         return ERROR_UserCancel;
     764           0 :                 if (err_NbWrite != OK)
     765             :                         return err_NbWrite;
     766           0 :                 ASSERT(err_Close != OK);
     767             :                 return err_Close;
     768             :         }
     769             : 
     770             :         //7) Remember the active filters and SortCaches
     771           0 :         std::vector<std::string> filters(filters_.size());
     772           0 :         for (size_t i = 0, n = filters_.size(); i < n; i++) {
     773           0 :                 filters[i] = filters_[i].first;
     774             :         }
     775           0 :         std::vector< std::pair<std::string, int> > oldSC;
     776           0 :         for (size_t i = 0, n = sortCaches_.size(); i < n; i++) {
     777           0 :                 int refCount = sortCaches_[i].second->incrRef(0);
     778           0 :                 if (refCount >= 0)
     779           0 :                         oldSC.push_back(std::make_pair(sortCaches_[i].first, refCount));
     780             :         }
     781             : 
     782             :         //8) Remove the old database
     783           0 :         if (Close() != OK) return ERROR_FileInUse;
     784           0 :         for (size_t i = 0, n = filenames.size(); i < n; i++) {
     785           0 :                 if (std::remove(filenames[i].c_str()) != 0) return ERROR_CompactRemove;
     786             :         }
     787             : 
     788             :         //9) Success: rename the files and open the new database
     789           0 :         for (size_t i = 0, n = filenames.size(); i < n; i++) {
     790           0 :                 const char* s1 = tmp_filenames[i].c_str();
     791           0 :                 const char* s2 = filenames[i].c_str();
     792           0 :                 std::rename(s1, s2);
     793             :         }
     794           0 :         errorT res = Open(dbtype, FMODE_Both, filename.c_str());
     795             : 
     796             :         //10) Re-create filters and SortCaches
     797           0 :         if (res == OK || res == ERROR_NameDataLoss) {
     798           0 :                 for (size_t i = 0, n = filters.size(); i < n; i++) {
     799           0 :                         filters_.push_back(
     800           0 :                             std::make_pair(filters[i], new Filter(numGames())));
     801             :                 }
     802           0 :                 for (size_t i = 0, n = oldSC.size(); i < n; i++) {
     803           0 :                         const std::string& criteria = oldSC[i].first;
     804           0 :                         SortCache* sc = SortCache::create(idx, nb, criteria.c_str());
     805           0 :                         if (sc != NULL) {
     806           0 :                                 sc->incrRef(oldSC[i].second);
     807           0 :                                 sortCaches_.push_back(std::make_pair(criteria, sc));
     808             :                         }
     809             :                 }
     810             :         }
     811             : 
     812             :         return res;
     813             : }
     814             : 
     815             : /**
     816             :  * Retrieve a SortCache object matching the supplied @e criteria.
     817             :  * A new SortCache with refCount equal to 0 is created if a suitable object is
     818             :  * not found in @e sortCaches_. Objects with refCount <= 0 are destroyed by the
     819             :  * @e releaseSortCache function independently from the provided @e criteria
     820             :  * argument (implementing a rudimentary garbage collector).
     821             :  * @param criteria: the list of fields by which games will be ordered.
     822             :  *                  Each field should be followed by '+' to indicate an
     823             :  *                  ascending order or by '-' for a descending order.
     824             :  * @returns a pointer to a SortCache object in case of success, NULL otherwise.
     825             :  */
     826       19817 : SortCache* scidBaseT::getSortCache(const char* criteria) {
     827       19817 :         ASSERT(criteria != NULL);
     828             : 
     829      710919 :         for (size_t i = 0, n = sortCaches_.size(); i < n; ++i) {
     830     2027907 :                 if (std::strcmp(criteria, sortCaches_[i].first.c_str()) == 0)
     831        4684 :                         return sortCaches_[i].second;
     832             :         }
     833             : 
     834       15133 :         SortCache* sc = SortCache::create(idx, nb, criteria);
     835       15133 :         if (sc != NULL)
     836         228 :                 sortCaches_.push_back(std::pair<std::string, SortCache*>(criteria, sc));
     837             : 
     838             :         return sc;
     839             : }
     840             : 
     841         795 : void scidBaseT::releaseSortCache(const char* criteria) {
     842         795 :         size_t i = 0;
     843       30548 :         while (i < sortCaches_.size()) {
     844       43437 :                 const char* tmp = sortCaches_[i].first.c_str();
     845       14479 :                 int decr = std::strcmp(criteria, tmp) ? 0 : -1;
     846       28958 :                 if (sortCaches_[i].second->incrRef(decr) <= 0) {
     847          74 :                         delete sortCaches_[i].second;
     848         370 :                         sortCaches_.erase(sortCaches_.begin() + i);
     849          74 :                         continue; //do not increment i
     850             :                 }
     851       14405 :                 i += 1;
     852             :         }
     853         795 : }
     854             : 
     855         797 : SortCache* scidBaseT::createSortCache(const char* criteria) {
     856         797 :         SortCache* sc = getSortCache(criteria);
     857         797 :         if (sc != NULL)
     858          80 :                 sc->incrRef(1);
     859             : 
     860         797 :         return sc;
     861             : }
     862             : 
     863       15900 : size_t scidBaseT::listGames(const char* criteria, size_t start, size_t count,
     864             :                             const HFilter& filter, gamenumT* destCont) {
     865       15900 :         const SortCache* sc = getSortCache(criteria);
     866       15900 :         if (sc == NULL)
     867             :                 return 0;
     868             : 
     869        1560 :         return sc->select(start, count, filter, destCont);
     870             : }
     871             : 
     872        3120 : size_t scidBaseT::sortedPosition(const char* criteria, const HFilter& filter,
     873             :                                  gamenumT gameId) {
     874        6240 :         ASSERT(filter != NULL && filter->size() <= numGames());
     875             : 
     876        6240 :         if (gameId >= numGames() || filter->get(gameId) == 0)
     877             :                 return INVALID_GAMEID;
     878             : 
     879        3120 :         SortCache* sc = getSortCache(criteria);
     880        3120 :         if (sc == NULL)
     881             :                 return INVALID_GAMEID;
     882             : 
     883        3120 :         return sc->sortedPosition(gameId, filter);
     884           2 : }

Generated by: LCOV version 1.12