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
|