LCOV - code coverage report
Current view: top level - src - namebase.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 59 61 96.7 %
Date: 2019-01-29 11:06:41 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2014-2017 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 SCID_NAMEBASE_H
      20             : #define SCID_NAMEBASE_H
      21             : 
      22             : #include "common.h"
      23             : #include "misc.h"
      24             : #include <algorithm>
      25             : #include <map>
      26             : #include <memory>
      27             : #include <vector>
      28             : 
      29             : /**
      30             :  * This class stores the database's names (players, events, sites and rounds).
      31             :  * Assigns a idNumberT (which will be used as reference) to each name.
      32             :  */
      33         150 : class NameBase {
      34             :         std::vector<std::unique_ptr<const char[]> > names_[NUM_NAME_TYPES];
      35             :         std::vector<eloT> eloV_;
      36             :         struct idxCmp {
      37   100070135 :                 bool operator()(const char* str1, const char* str2) const {
      38             :                         // *** Compatibility ***
      39             :                         // Older code used a custom StrTree class with a peculiar sorting:
      40             :                         // - the first char was interpreted as an unsigned char;
      41             :                         // - the remaining part was compared with the function
      42             :                         // strComapare(),
      43             :                         //   which converts the chars to ints, and is not consistent with
      44             :                         //   the standard function strcmp().
      45             :                         // The old StrTree class did also have unpredictable behaviors when
      46             :                         // fed with names not sorted according to that criteria, for example
      47             :                         // it could create Namebase objects with duplicate entries.
      48             :                         // ***
      49   100070135 :                         if (*str1 == *str2)
      50    88664937 :                                 return strCompare(str1, str2) < 0;
      51             : 
      52    11405198 :                         return static_cast<uint>(*str1) < static_cast<uint>(*str2);
      53             :                 }
      54             :         };
      55             :         std::map<const char*, idNumberT, idxCmp> idx_[NUM_NAME_TYPES];
      56             : 
      57             : public:
      58             :         /**
      59             :          * A NameBase stores the max ELO for each player. This functions updates
      60             :          * the max ELO of a player if it's greater than the previous one.
      61             :          * @param id:  a valid idNumberT corresponding to a NAME_PLAYER name.
      62             :          * @param elo: the ELO.
      63             :          */
      64       32714 :         void AddElo(idNumberT id, eloT elo) {
      65       32714 :                 ASSERT(id < GetNumNames(NAME_PLAYER));
      66       32714 :                 if (elo > eloV_[id])
      67        1360 :                         eloV_[id] = elo;
      68       32714 :         }
      69             : 
      70             :         /**
      71             :          * Add a name (string) to the NameBase.
      72             :          * If the name already exists the corresponding ID is returned.
      73             :          * @param nt:      @e nameT type of the name to add.
      74             :          * @param name:    the name to add.
      75             :          * @param MAX_LEN: the max length for names of type @e nt
      76             :          * @param MAX_ID:  the max ID allowed for names of type @e nt
      77             :          * @returns
      78             :          * - on success, a @e std::pair containing OK and the ID.
      79             :          * - on failure, a @e std::pair containing an error code and 0.
      80             :          */
      81     4795158 :         std::pair<errorT, idNumberT> addName(nameT nt, const char* name,
      82             :                                              size_t MAX_LEN, idNumberT MAX_ID) {
      83     4795158 :                 ASSERT(IsValidNameType(nt) && name != NULL);
      84             : 
      85     4795158 :                 auto exists = idx_[nt].lower_bound(name);
      86    14385257 :                 if (exists != idx_[nt].end() &&
      87     9590099 :                     !idx_[nt].key_comp()(name, exists->first))
      88     2434284 :                         return std::make_pair(OK, exists->second);
      89             : 
      90     2360874 :                 const size_t nameLen = strlen(name);
      91     2360874 :                 if (nameLen > MAX_LEN)
      92         340 :                         return std::make_pair(ERROR_NameTooLong, 0);
      93             : 
      94     2360534 :                 if (names_[nt].size() >= MAX_ID)
      95           4 :                         return std::make_pair(ERROR_NameLimit, 0);
      96             : 
      97     2360530 :                 char* buf = new char[nameLen + 1];
      98     2360530 :                 std::copy_n(name, nameLen + 1, buf);
      99     2360530 :                 idNumberT newID = static_cast<idNumberT>(names_[nt].size());
     100     2360530 :                 names_[nt].emplace_back(buf);
     101     2360530 :                 idx_[nt].emplace_hint(exists, buf, newID);
     102             : 
     103     2360530 :                 if (nt == NAME_PLAYER)
     104     1048895 :                         eloV_.push_back(0);
     105             : 
     106     2360530 :                 return std::make_pair(OK, newID);
     107             :         }
     108             : 
     109             :         /**
     110             :          * Frees memory, leaving the object empty.
     111             :          */
     112          14 :         void Clear() { *this = NameBase(); }
     113             : 
     114             :         /**
     115             :          * @returns references to the NameBase's containers.
     116             :          * (must be used only to read names from files)
     117             :          */
     118             :         std::tuple<decltype(idx_) &, decltype(names_) &, decltype(eloV_) &>
     119           7 :         getData() {
     120             :                 return std::tuple<decltype(idx_)&, decltype(names_)&, decltype(eloV_)&>(
     121           7 :                     idx_, names_, eloV_);
     122             :         }
     123             : 
     124             :         /**
     125             :          * @param id: a valid idNumberT corresponding to a NAME_PLAYER name.
     126             :          * @returns the max ELO of a player.
     127             :          */
     128       55348 :         eloT GetElo(idNumberT id) const {
     129       55348 :                 ASSERT(id < GetNumNames(NAME_PLAYER));
     130       55348 :                 return eloV_[id];
     131             :         }
     132             : 
     133             :         /**
     134             :          * Get the first few matches of a name prefix.
     135             :          * @param nt:         @e nameT type of the name to be searched.
     136             :          * @param str:        name prefix be searched.
     137             :          * @param maxMatches: the max number of ID to return
     138             :          * @returns a vector containing the ID of the matching names.
     139             :          */
     140           1 :         std::vector<idNumberT> getFirstMatches(nameT nt, const char* str,
     141             :                                                size_t maxMatches) const {
     142           1 :                 ASSERT(IsValidNameType(nt) && str != NULL);
     143             : 
     144           1 :                 std::vector<idNumberT> res;
     145           1 :                 size_t len = strlen(str);
     146           5 :                 for (auto it = idx_[nt].lower_bound(str);
     147           5 :                      it != idx_[nt].end() && res.size() < maxMatches; ++it) {
     148           4 :                         const char* s = it->first;
     149           4 :                         if (strlen(s) < len || !std::equal(str, str + len, s))
     150           0 :                                 break;
     151           4 :                         res.emplace_back(it->second);
     152             :                 }
     153           1 :                 return res;
     154             :         }
     155             : 
     156             :         /**
     157             :          * Retrieve a name.
     158             :          * @param nt: the valid @e nameT type of the name to retrieve.
     159             :          * @param id: the valid ID of the name to retrieve.
     160             :          * @returns the name corresponding to @e id.
     161             :          */
     162     2603626 :         const char* GetName(nameT nt, idNumberT id) const {
     163     2603626 :                 ASSERT(IsValidNameType(nt) && id < GetNumNames(nt));
     164     2603626 :                 return names_[nt][id].get();
     165             :         }
     166             : 
     167             :         /**
     168             :          * @returns a reference to a container with all the names and IDs (given as
     169             :          * std::pair<const char*, idNumberT>).
     170             :          */
     171          12 :         const decltype(idx_) & getNames() const { return idx_; }
     172             : 
     173             :         /**
     174             :          * @param nt: a valid @e nameT type.
     175             :          * @returns the first invalid idNumberT (which is equal to the number of
     176             :          * names stored).
     177             :          */
     178     2691764 :         idNumberT GetNumNames(nameT nt) const {
     179     2691764 :                 ASSERT(IsValidNameType(nt));
     180     2691764 :                 return static_cast<idNumberT>(names_[nt].size());
     181             :         }
     182             : 
     183             :         /**
     184             :          * Finds an exact full, case-sensitive name.
     185             :          * @param nt:         @e nameT type of the name to be searched.
     186             :          * @param str:        name to be be searched.
     187             :          * @param[out] idPtr: pointer which will receive the ID of the name.
     188             :          * @returns OK or ERROR_NameNotFound if the name does not exists.
     189             :          */
     190         112 :         errorT FindExactName(nameT nt, const char* str, idNumberT* idPtr) const {
     191         112 :                 ASSERT(IsValidNameType(nt) && str != NULL && idPtr != NULL);
     192             : 
     193         112 :                 auto it = idx_[nt].find(str);
     194         112 :                 if (it != idx_[nt].end()) {
     195         112 :                         *idPtr = (*it).second;
     196         112 :                         return OK;
     197             :                 }
     198           0 :                 return ERROR_NameNotFound;
     199             :         }
     200             : 
     201             :         /**
     202             :          * For every name generates a 32bit hash with the first 4 chars.
     203             :          * @param nt: @e nameT type of the names.
     204             :          * @returns a vector containing the hashes.
     205             :          */
     206             :         std::vector<uint32_t> generateHashMap(nameT nt) const {
     207             :                 std::vector<uint32_t> res(names_[nt].size());
     208             :                 std::transform(names_[nt].begin(), names_[nt].end(), res.begin(),
     209             :                                [](const std::unique_ptr<const char[]>& name) {
     210             :                                        return strStartHash(name.get());
     211             :                                });
     212             :                 return res;
     213             :         }
     214             : 
     215             :         /**
     216             :          * Validate a @e nameT type.
     217             :          * @param nt: @e nameT type to be validated.
     218             :          * @returns true if @e nt is valid.
     219             :          */
     220    10090661 :         static bool IsValidNameType(nameT nt) { return (nt < NUM_NAME_TYPES); }
     221             : 
     222             :         /**
     223             :          * Match a string to a nameT.
     224             :          * To match, the string should be a prefix of "player", "event", "site" or
     225             :          * "round", or be a superstring of it, e.g. "player ...."
     226             :          * @param str: the string to be matched.
     227             :          * @returns a valid nameT, or NAME_INVALID.
     228             :          */
     229             :         static nameT NameTypeFromString(const char* str) {
     230             :                 if (*str == '\0')
     231             :                         return NAME_INVALID;
     232             :                 if (strIsAlphaPrefix(str, "player"))
     233             :                         return NAME_PLAYER;
     234             :                 if (strIsAlphaPrefix(str, "event"))
     235             :                         return NAME_EVENT;
     236             :                 if (strIsAlphaPrefix(str, "site"))
     237             :                         return NAME_SITE;
     238             :                 if (strIsAlphaPrefix(str, "round"))
     239             :                         return NAME_ROUND;
     240             :                 if (strIsAlphaPrefix("player", str))
     241             :                         return NAME_PLAYER;
     242             :                 if (strIsAlphaPrefix("event", str))
     243             :                         return NAME_EVENT;
     244             :                 if (strIsAlphaPrefix("site", str))
     245             :                         return NAME_SITE;
     246             :                 if (strIsAlphaPrefix("round", str))
     247             :                         return NAME_ROUND;
     248             :                 return NAME_INVALID;
     249             :         }
     250             : };
     251             : 
     252             : #endif // SCID_NAMEBASE_H

Generated by: LCOV version 1.13