LCOV - code coverage report
Current view: top level - src - scidbase.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 21 41 51.2 %
Date: 2019-01-29 11:06:41 Functions: 8 14 57.1 %

          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             : #ifndef SCIDBASE_H
      20             : #define SCIDBASE_H
      21             : 
      22             : #include "bytebuf.h"
      23             : #include "codec.h"
      24             : #include "containers.h"
      25             : #include "fastgame.h"
      26             : #include "game.h"
      27             : #include "index.h"
      28             : #include "namebase.h"
      29             : #include "tree.h"
      30             : #include <array>
      31             : #include <vector>
      32             : #include <memory>
      33             : 
      34             : class SortCache;
      35             : 
      36             : 
      37             : const gamenumT INVALID_GAMEID = 0xffffffff;
      38             : 
      39             : struct scidBaseT {
      40             :         struct Stats {
      41             :                 uint flagCount[IndexEntry::IDX_NUM_FLAGS]; // Num of games with each flag set.
      42             :                 dateT minDate;
      43             :                 dateT maxDate;
      44             :                 uint64_t nYears;
      45             :                 uint64_t sumYears;
      46             :                 uint nResults [NUM_RESULT_TYPES];
      47             :                 uint nRatings;
      48             :                 uint64_t sumRatings;
      49             :                 uint minRating;
      50             :                 uint maxRating;
      51             : 
      52             :                 Stats(const scidBaseT* dbase);
      53             : 
      54             :                 struct Eco {
      55             :                         uint count;
      56             :                         uint results [NUM_RESULT_TYPES];
      57             : 
      58             :                         Eco();
      59             :                 };
      60             :                 const Eco* getEcoStats(const char* ecoStr) const;
      61             : 
      62             :         private:
      63             :                 Eco ecoEmpty_;
      64             :                 Eco ecoValid_;
      65             :                 Eco ecoStats_ [(1 + (1<<16)/131)*27];
      66             :                 Eco ecoGroup1_[(1 + (1<<16)/131)/100];
      67             :                 Eco ecoGroup2_[(1 + (1<<16)/131)/10];
      68             :                 Eco ecoGroup3_[(1 + (1<<16)/131)];
      69             :         };
      70             : 
      71           0 :         struct TreeStat {
      72             :                 colorT toMove;
      73             :                 std::string SAN;
      74             :                 int resultW, resultD, resultB;
      75             :                 double exp;
      76             :                 int ngames, nexp;
      77             : 
      78             :         public:
      79             :                 TreeStat();
      80             :                 void add(int result, int eloW, int eloB);
      81           0 :                 bool operator<(const TreeStat& cmp) const { return ngames > cmp.ngames; }
      82             : 
      83             :         private:
      84             :                 static double expVect_[1600];
      85             :         };
      86             : 
      87             :         scidBaseT();
      88             :         ~scidBaseT();
      89             : 
      90             :         errorT Open(ICodecDatabase::Codec dbtype,
      91             :                     fileModeT mode,
      92             :                     const char* filename = 0,
      93             :                     const Progress& progress = Progress());
      94             : 
      95             :         errorT Close ();
      96             : 
      97             :         const std::string& getFileName() const { return fileName_; }
      98          12 :         bool isReadOnly() const { return fileMode_ == FMODE_ReadOnly; }
      99        6439 :         gamenumT numGames() const { return idx->GetNumGames(); }
     100             : 
     101             :         /// Returns a vector of tag pairs containing extra information about the
     102             :         /// database (type, description, autoload, etc..)
     103             :         std::vector<std::pair<const char*, std::string> > getExtraInfo() const {
     104             :                 return codec_->getExtraInfo();
     105             :         }
     106             : 
     107             :         /// Store an extra information about the database (type, description, etc..)
     108             :         errorT setExtraInfo(const char* tagname, const char* new_value) {
     109             :                 // TODO: move the code to CodecMemory and CodecSCID4
     110             :                 if (std::strcmp(tagname, "type") == 0)
     111             :                         return idx->SetType(strGetUnsigned(new_value));
     112             : 
     113             :                 if (codec_->getType() == ICodecDatabase::SCID4) {
     114             :                         if (std::strcmp(tagname, "description") == 0)
     115             :                                 return idx->SetDescription(new_value);
     116             : 
     117             :                         if (std::strcmp(tagname, "autoload") == 0)
     118             :                                 return idx->SetAutoLoad(strGetUnsigned(new_value));
     119             : 
     120             :                         auto len = std::strlen(tagname);
     121             :                         if (len == 5 && std::equal(tagname, tagname + 4, "flag")) {
     122             :                                 uint flagType = IndexEntry::CharToFlag(tagname[4]);
     123             :                                 if (flagType != 0 &&
     124             :                                     idx->GetCustomFlagDesc(flagType) != nullptr)
     125             :                                         return idx->SetCustomFlagDesc(flagType, new_value);
     126             :                         }
     127             :                 }
     128             :                 return ERROR_CodecUnsupFeat;
     129             :         }
     130             : 
     131        1390 :         const IndexEntry* getIndexEntry(gamenumT g) const {
     132        1390 :                 return idx->GetEntry(g);
     133             :         }
     134          34 :         const IndexEntry* getIndexEntry_bounds(gamenumT g) const {
     135          34 :                 if (g < numGames()) return getIndexEntry(g);
     136           4 :                 return 0;
     137             :         }
     138       15137 :         const NameBase* getNameBase() const {
     139       15137 :                 return nb_;
     140             :         }
     141           0 :         FastGame getGame(const IndexEntry* ie) const {
     142           0 :                 uint length = ie->GetLength();
     143           0 :                 const byte* b = codec_->getGameData(ie->GetOffset(), length);
     144           0 :                 if (b == 0) length = 0; // Error
     145           0 :                 return FastGame::Create(b, b + length);
     146             :         }
     147          18 :         errorT getGame(const IndexEntry* ie, ByteBuffer* destBuf) const {
     148          18 :                 uint length = ie->GetLength();
     149          18 :                 const byte* b = codec_->getGameData(ie->GetOffset(), length);
     150          18 :                 if (b == 0) return ERROR_FileRead;
     151             :                 // The data for the game is not actually copied into the bytebuffer, which would
     152             :                 // be slower and a waste of time if the bytebuffer is not going to be modified.
     153          18 :                 destBuf->ProvideExternal(const_cast<byte*>(b), length);
     154          18 :                 return OK;
     155             :         }
     156          12 :         errorT getGame(const IndexEntry& ie, Game& dest) const {
     157          24 :                 ByteBuffer buf(0);
     158          12 :                 auto res = getGame(&ie, &buf);
     159          24 :                 return (res != OK) ? res : dest.Decode(&buf, GAME_DECODE_ALL);
     160             :         }
     161             : 
     162             :         errorT importGame(const scidBaseT* srcBase, uint gNum);
     163             :         errorT importGames(const scidBaseT* srcBase, const HFilter& filter,
     164             :                            const Progress& progress);
     165             :         errorT importGames(ICodecDatabase::Codec dbtype, const char* filename,
     166             :                            const Progress& progress, std::string& errorMsg);
     167             : 
     168             :         /**
     169             :          * Add or replace a game into the database.
     170             :          * @param game: valid pointer to a Game object with the data of the game.
     171             :          * @param replacedGameId: id of the game to replace.
     172             :          *                        If >= numGames(), a new game will be added.
     173             :          * @returns OK if successful or an error code.
     174             :          */
     175             :         errorT saveGame(Game* game, gamenumT replacedGameId = INVALID_GAMEID);
     176             :         // TODO: private:
     177             :         errorT saveGameHelper(Game* game, gamenumT gameId);
     178             : 
     179             : 
     180             :         bool getFlag(uint flag, uint gNum) const {
     181             :                 return idx->GetEntry(gNum)->GetFlag (flag);
     182             :         }
     183             :         errorT setFlag(bool value, uint flag, uint gNum);
     184             :         errorT setFlag(bool value, uint flag, const HFilter& filter);
     185             :         errorT invertFlag(uint flag, uint gNum);
     186             :         errorT invertFlag(uint flag, const HFilter& filter);
     187             : 
     188             :         /**
     189             :          * A Filter is a selection of games, usually obtained searching the
     190             :          * database. A new Filter is created calling the function newFilter()
     191             :          * and must be released calling the function deleteFilter().
     192             :          * A composed Filter is a special construct created combining two Filters
     193             :          * and includes only the games contained in both Filters.
     194             :          * A composed Filter should NOT be released.
     195             :          */
     196             :         std::string newFilter();
     197             :         std::string composeFilter(const std::string& mainFilter,
     198             :                                   const std::string& maskFilter) const;
     199             :         void deleteFilter(const char* filterId);
     200       36551 :         HFilter getFilter(const std::string& filterId) const {
     201       36551 :                 return getFilterHelper(filterId, false);
     202             :         }
     203             :         HFilter getMainFilter(const std::string& filterId) const {
     204             :                 return getFilterHelper(filterId, true);
     205             :         }
     206             : 
     207             :         const Stats& getStats() const;
     208             :         std::vector<scidBaseT::TreeStat> getTreeStat(const HFilter& filter);
     209             :         uint getNameFreq (nameT nt, idNumberT id) {
     210             :                 if (nameFreq_[nt].size() == 0)
     211             :                         nameFreq_ = idx->calcNameFreq(*getNameBase());
     212             :                 return nameFreq_[nt][id];
     213             :         }
     214             : 
     215             :         errorT getCompactStat(unsigned long long* n_deleted,
     216             :                               unsigned long long* n_unused,
     217             :                               unsigned long long* n_sparse,
     218             :                               unsigned long long* n_badNameId);
     219             :         errorT compact(const Progress& progress);
     220             : 
     221             : 
     222             :         /**
     223             :          * Increment the reference count of a SortCache object matching @e criteria.
     224             :          * @param criteria: the list of fields by which games will be ordered.
     225             :          *                  Each field should be followed by '+' to indicate an
     226             :          *                  ascending order or by '-' for a descending order.
     227             :          * @returns a pointer to a SortCache object in case of success, NULL
     228             :          * otherwise.
     229             :          */
     230             :         SortCache* createSortCache(const char* criteria);
     231             : 
     232             :         /**
     233             :          * Decrement the reference count of the SortCache object matching @e
     234             :          * criteria. Cached objects with refCount <= 0 are destroyed independently
     235             :          * from the value of @e criteria.
     236             :          * @param criteria: the list of fields by which games will be ordered.
     237             :          *                  Each field should be followed by '+' to indicate an
     238             :          *                  ascending order or by '-' for a descending order.
     239             :          */
     240             :         void releaseSortCache(const char* criteria);
     241             : 
     242             :         /**
     243             :          * Retrieve a list of ordered game indexes sorted by @e criteria.
     244             :          * This function will be much faster if a SortCache object matching @e
     245             :          * criteria already exists (previously created with @e createSortCache).
     246             :          * @param criteria: the list of fields by which games will be ordered.
     247             :          *                  Each field should be followed by '+' to indicate an
     248             :          *                  ascending order or by '-' for a descending order.
     249             :          * @param start:    the offset of the first row to return.
     250             :          *                  The offset of the initial row is 0.
     251             :          * @param count:    maximum number of rows to return.
     252             :          * @param filter:   a reference to a valid (!= NULL) HFilter object.
     253             :          *                  Games not included into the filter will be ignored.
     254             :          * @param[out] destCont: valid pointer to an array where the sorted list of
     255             :          *                       games will be stored (should be able to contain at
     256             :          *                       least @e count elements).
     257             :          * @returns the number of games' ids stored into @e destCont.
     258             :          */
     259             :         size_t listGames(const char* criteria, size_t start, size_t count,
     260             :                          const HFilter& filter, gamenumT* destCont);
     261             : 
     262             :         /**
     263             :          * Get the sorted position of a game.
     264             :          * This function will be much faster if a SortCache object matching @e
     265             :          * criteria already exists (previously created with @e createSortCache).
     266             :          * @param criteria: the list of fields by which games will be ordered.
     267             :          *                  Each field should be followed by '+' to indicate an
     268             :          *                  ascending order or by '-' for a descending order.
     269             :          * @param filter:   a reference to a valid (!= NULL) HFilter object.
     270             :          *                  Games not included into the filter will be ignored.
     271             :          * @param gameId:   the id of the game.
     272             :          * @returns the sorted position of @e gameId.
     273             :          */
     274             :         size_t sortedPosition(const char* criteria, const HFilter& filter,
     275             :                               gamenumT gameId);
     276             : 
     277             :         void setDuplicates(uint* duplicates) {
     278             :                 if (duplicates_ != NULL) { delete[] duplicates_; duplicates_ = NULL; }
     279             :                 duplicates_ = duplicates;
     280             :         }
     281             :         uint getDuplicates(gamenumT gNum) {
     282             :                 return (duplicates_ == NULL) ? 0 : duplicates_[gNum];
     283             :         }
     284             : 
     285             :         /**
     286             :          * Transform the IndexEntries of the games included in @e hfilter.
     287             :          * The @e entry_op must accept a IndexEntry& parameter and return true when
     288             :          * the IndexEntry was modified.
     289             :          * @param hfilter:  HFilter containing the games to be transformed.
     290             :          * @param progress: a Progress object used for GUI communications.
     291             :          * @param entry_op: operator that will be applied to games' IndexEntry.
     292             :          * @returns a std::pair containing OK (or an error code) and the number of
     293             :          * games modified.
     294             :          */
     295             :         template <typename TOper>
     296             :         std::pair<errorT, size_t>
     297             :         transformIndex(HFilter hfilter, const Progress& progress, TOper entry_op) {
     298             :                 beginTransaction();
     299             :                 auto res = transformIndex_(hfilter, progress, entry_op);
     300             :                 auto err = endTransaction();
     301             :                 res.first = (res.first == OK) ? err : res.first;
     302             :                 return res;
     303             :         }
     304             : 
     305             :         /**
     306             :          * Transform the names of the games included in @e hfilter.
     307             :          * The function @e getID maps all the old idNumberT to the new idNumberT.
     308             :          * It's invoked for each game and must accept as parameters a idNumberT and
     309             :          * a const IndexEntry&; must return the (eventually different) idNumberT.
     310             :          * @param nt:       type of the names to be modified.
     311             :          * @param hfilter:  HFilter containing the games to be transformed.
     312             :          * @param progress: a Progress object used for GUI communications.
     313             :          * @param newNames: optional vector of names to be added to the database.
     314             :          * @param fnInit:   function that is invoked before beginning the
     315             :          *                  transformation; must accept a vector that contains the
     316             :          *                  idNumberTs of the names in @e newNames.
     317             :          * @param getID:    function that maps the old idNumberTs to the new ones.
     318             :          * @returns a std::pair containing OK (or an error code) and the number of
     319             :          * games modified.
     320             :          */
     321             :         template <typename TInitFunc, typename TMapFunc>
     322             :         std::pair<errorT, size_t>
     323             :         transformNames(nameT nt, HFilter hfilter, const Progress& progress,
     324             :                        const std::vector<std::string>& newNames, TInitFunc fnInit,
     325             :                        TMapFunc getID);
     326             : 
     327             :         // TODO: private:
     328             :         /**
     329             :          * This function must be called before modifying the games of the database.
     330             :          * Currently this function do not guarantees that the database is not altered
     331             :          * in case of errors.
     332             :          */
     333             :         void beginTransaction();
     334             : 
     335             :         // TODO: private:
     336             :         /**
     337             :          * Update caches and flush the database's files.
     338             :          * This function must be called after changing one or more games.
     339             :          * @param gameId: id of the modified game
     340             :          *                INVALID_GAMEID to update all games.
     341             :          * @returns OK if successful or an error code.
     342             :          */
     343             :         errorT endTransaction(gamenumT gameId = INVALID_GAMEID);
     344             : 
     345             : public:
     346             :         Index* idx;       // the Index file in memory for this base.
     347             :         bool inUse;       // true if the database is open (in use).
     348             :         treeT tree;
     349             :         TreeCache treeCache;
     350             :         ByteBuffer* bbuf;
     351             :         Filter* dbFilter;
     352             :         Filter* treeFilter;
     353             : 
     354             : 
     355             :         //TODO: this vars do not belong to scidBaseT class
     356             :         Game* game;       // the active game for this base.
     357             :         int gameNumber;   // game number of active game.
     358             :         bool gameAltered; // true if game is modified
     359             :         UndoRedo<Game, 100> gameAlterations;
     360             :         std::pair<Game*, bool> deprecated_push_pop;
     361             : 
     362             : private:
     363             :         std::unique_ptr<ICodecDatabase> codec_;
     364             :         NameBase* nb_;
     365             :         std::string fileName_; // File name without ".si" suffix
     366             :         fileModeT fileMode_; // Read-only, write-only, or both.
     367             :         std::vector< std::pair<std::string, Filter*> > filters_;
     368             :         mutable Stats* stats_;
     369             :         std::array<std::vector<int>, NUM_NAME_TYPES> nameFreq_;
     370             :         uint* duplicates_; // For each game: idx of duplicate game + 1 (0 if there is no duplicate).
     371             :         std::vector< std::pair<std::string, SortCache*> > sortCaches_;
     372             : 
     373             : private:
     374             :         void clear();
     375             :         errorT importGameHelper(const scidBaseT* sourceBase, uint gNum);
     376             : 
     377             :         Filter* fetchFilter(const std::string& filterId) const;
     378             :         HFilter getFilterHelper(const std::string& filterId,
     379             :                                 bool unmasked = false) const;
     380             :         SortCache* getSortCache(const char* criteria);
     381             : 
     382             :         /**
     383             :          * Apply a transform operator to games' IndexEntry included in @e hfilter.
     384             :          * The @entry_op should accept a IndexEntry& parameter and return true when
     385             :          * the IndexEntry was modified.
     386             :          * @param hfilter:  HFilter containing the games to be transformed.
     387             :          * @param progress: a Progress object used for GUI communications.
     388             :          * @param entry_op: operator that will be applied to games' IndexEntry.
     389             :          * @returns a std::pair containing OK (or an error code) and the number of
     390             :          * games modified.
     391             :          */
     392             :         template <typename TOper>
     393             :         std::pair<errorT, size_t>
     394             :         transformIndex_(HFilter hfilter, const Progress& progress, TOper entry_op) {
     395             :                 size_t nCorrections = 0;
     396             :                 size_t iProg = 0;
     397             :                 size_t totProg = hfilter->size();
     398             :                 for (auto& gnum : hfilter) {
     399             :                         if ((++iProg % 8192 == 0) && !progress.report(iProg, totProg))
     400             :                                 return std::make_pair(ERROR_UserCancel, nCorrections);
     401             : 
     402             :                         IndexEntry newIE = *getIndexEntry(gnum);
     403             :                         if (!entry_op(newIE))
     404             :                                 continue;
     405             : 
     406             :                         auto err = codec_->saveIndexEntry(newIE, gnum);
     407             :                         if (err != OK)
     408             :                                 return std::make_pair(err, nCorrections);
     409             : 
     410             :                         ++nCorrections;
     411             :                 }
     412             :                 return std::make_pair(OK, nCorrections);
     413             :         }
     414             : };
     415             : 
     416           0 : inline void scidBaseT::TreeStat::add(int result, int eloW, int eloB) {
     417           0 :         ngames++;
     418           0 :         double r = 0;
     419           0 :         switch (result) {
     420           0 :                 case RESULT_White: resultW++; r = 1; break;
     421           0 :                 case RESULT_Draw: resultD++; r = 0.5; break;
     422           0 :                 case RESULT_Black: resultB++; break;
     423           0 :                 default: return;
     424             :         }
     425           0 :         if (eloW == 0 || eloB == 0) return;
     426           0 :         int eloDiff = eloB - eloW;
     427           0 :         if (eloDiff < 800 && eloDiff >= -800) {
     428           0 :                 exp += r - expVect_[eloDiff+800];
     429           0 :                 nexp++;
     430             :         }
     431             : }
     432             : 
     433             : inline errorT scidBaseT::invertFlag(uint flag, uint gNum) {
     434             :         return setFlag(! getFlag(flag, gNum), flag, gNum);
     435             : }
     436             : 
     437             : inline errorT scidBaseT::invertFlag(uint flag, const HFilter& filter) {
     438             :         errorT res = OK;
     439             :         for (gamenumT i = 0, n = numGames(); i < n; i++) {
     440             :                 if (filter != 0 && filter->get(i) == 0) continue;
     441             :                 res = invertFlag(flag, i);
     442             :                 if (res != OK) return res;
     443             :         }
     444             :         return res;
     445             : }
     446             : 
     447             : inline errorT scidBaseT::setFlag(bool value, uint flag, uint gNum){
     448             :         ASSERT(gNum < idx->GetNumGames());
     449             :         IndexEntry* ie = idx->FetchEntry (gNum);
     450             :         ie->SetFlag (flag, value);
     451             :         errorT res = idx->WriteEntry(ie, gNum);
     452             :         if (stats_ != NULL) { delete stats_; stats_ = NULL;}
     453             :         // TODO: necessary only for sortcaches with SORTING_deleted (and SORTING_flags when implemented)
     454             :         // idx->IndexUpdated(gNum);
     455             :         return res;
     456             : }
     457             : 
     458             : inline errorT scidBaseT::setFlag(bool value, uint flag, const HFilter& filter) {
     459             :         errorT res = OK;
     460             :         for (gamenumT gNum = 0, n = numGames(); gNum < n; gNum++) {
     461             :                 if (filter != 0 && filter->get(gNum) == 0) continue;
     462             :                 res = setFlag(value, flag, gNum);
     463             :                 if (res != OK) return res;
     464             :         }
     465             :         return res;
     466             : }
     467             : 
     468             : 
     469             : template <typename TInitFunc, typename TMapFunc>
     470             : std::pair<errorT, size_t>
     471             : scidBaseT::transformNames(nameT nt, HFilter hfilter, const Progress& progress,
     472             :                           const std::vector<std::string>& newNames,
     473             :                           TInitFunc initFunc, TMapFunc getID) {
     474             :         beginTransaction();
     475             : 
     476             :         std::vector<idNumberT> nameIDs(newNames.size());
     477             :         auto it = nameIDs.begin();
     478             :         for (auto& name : newNames) {
     479             :                 auto id = codec_->addName(nt, name.c_str());
     480             :                 if (id.first != OK) {
     481             :                         endTransaction();
     482             :                         return std::make_pair(id.first, size_t(0));
     483             :                 }
     484             :                 *it++ = id.second;
     485             :         }
     486             : 
     487             :         initFunc(nameIDs);
     488             : 
     489             :         auto fnGet = [](nameT nt, const IndexEntry& ie) {
     490             :                 switch (nt) { // clang-format off
     491             :                 case NAME_PLAYER: return ie.GetWhite();
     492             :                 case NAME_EVENT:  return ie.GetEvent();
     493             :                 case NAME_SITE:   return ie.GetSite();
     494             :                 } // clang-format on
     495             :                 ASSERT(nt == NAME_ROUND);
     496             :                 return ie.GetRound();
     497             :         };
     498             :         auto fnSet = [](nameT nt, IndexEntry& ie, idNumberT newID) {
     499             :                 switch (nt) { // clang-format off
     500             :                 case NAME_PLAYER: return ie.SetWhite(newID);
     501             :                 case NAME_EVENT:  return ie.SetEvent(newID);
     502             :                 case NAME_SITE:   return ie.SetSite(newID);
     503             :                 } // clang-format on
     504             :                 ASSERT(nt == NAME_ROUND);
     505             :                 return ie.SetRound(newID);
     506             :         };
     507             :         auto res = transformIndex_(hfilter, progress, [&](IndexEntry& ie) {
     508             :                 const IndexEntry& ie_const = ie;
     509             :                 auto oldID = fnGet(nt, ie);
     510             :                 auto newID = getID(oldID, ie_const);
     511             :                 bool b1 = (oldID != newID);
     512             :                 idNumberT newBlack = 0;
     513             :                 bool b2 = (nt == NAME_PLAYER);
     514             :                 if (b2) {
     515             :                         auto oldBlack = ie.GetBlack();
     516             :                         newBlack = getID(oldBlack, ie_const);
     517             :                         b2 = (oldBlack != newBlack);
     518             :                 }
     519             :                 if (!b1 && !b2)
     520             :                         return false;
     521             : 
     522             :                 if (b1)
     523             :                         fnSet(nt, ie, newID);
     524             :                 if (b2)
     525             :                         ie.SetBlack(newBlack);
     526             :                 return true;
     527             :         });
     528             : 
     529             :         auto err = endTransaction();
     530             :         res.first = (res.first == OK) ? err : res.first;
     531             :         return res;
     532             : }
     533             : 
     534             : #endif

Generated by: LCOV version 1.13