Line data Source code
1 : /*
2 : * Copyright (c) 1999-2002 Shane Hudson
3 : * Copyright (c) 2006-2009 Pascal Georges
4 : * Copyright (C) 2014-2016 Fulvio Benini
5 :
6 : * This file is part of Scid (Shane's Chess Information Database).
7 : *
8 : * Scid is free software: you can redistribute it and/or modify
9 : * it under the terms of the GNU General Public License as published by
10 : * the Free Software Foundation.
11 : *
12 : * Scid is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU General Public License
18 : * along with Scid. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #ifndef SCID_INDEX_H
22 : #define SCID_INDEX_H
23 :
24 : #include "common.h"
25 : #include "containers.h"
26 : #include "date.h"
27 : #include "indexentry.h"
28 : #include "filebuf.h"
29 : #include "hfilter.h"
30 : #include <array>
31 : #include <string>
32 : #include <vector>
33 : #include <cstring>
34 :
35 : class NameBase;
36 :
37 : //////////////////////////////////////////////////////////////////////
38 : // Index: Constants
39 :
40 : const char INDEX_SUFFIX[] = ".si4";
41 : const char OLD_INDEX_SUFFIX[] = ".si3";
42 :
43 : // Descriptions can be up to 107 bytes long.
44 : const uint SCID_DESC_LENGTH = 107;
45 : const uint CUSTOM_FLAG_DESC_LENGTH = 8;
46 : const uint CUSTOM_FLAG_MAX = 6;
47 :
48 : // Header on-disk size: magic=8, version=2, numGames=3, baseType=4, autoLoad=3
49 : // Description length = 111 bytes including trailing '\0'.
50 : // Custom flag desc length = 9 bytes including trailing '\0'.
51 : // So total is 128 bytes + 9*6 = 182 bytes for the whole header.
52 : const uint INDEX_HEADER_SIZE = 8 + 2 + 3 + 4 + 3 + SCID_DESC_LENGTH + 1 + (CUSTOM_FLAG_DESC_LENGTH+1) * CUSTOM_FLAG_MAX;
53 : const uint OLD_INDEX_HEADER_SIZE = INDEX_HEADER_SIZE - (CUSTOM_FLAG_DESC_LENGTH+1) * CUSTOM_FLAG_MAX;
54 :
55 : //////////////////////////////////////////////////////////////////////
56 : // Index: Class Definition
57 :
58 : class Index
59 : {
60 : private:
61 : // The complete index will be loaded in memory and can be pretty huge.
62 : // To avoid the slow reallocation when adding games we split the data in chunks.
63 : // CHUNKSHIFT is the base-2 logarithm of the number of index entries allocated as one chunk.
64 : // i.e 16 = 2^16 = 65536 (total size of one chunk: 65536*48 = 3MB)
65 : VectorChunked<IndexEntry, 16> entries_; // A two-level array of the entire index.
66 : Filebuf* FilePtr; // filehandle for opened index file.
67 : fileModeT fileMode_; // Mode: e.g. FILE_WRITEONLY
68 : int nInvalidNameId_;
69 : gamenumT seqWrite_;
70 :
71 : struct { // one at the start of the index file.
72 : versionT version; // version number. 2 bytes.
73 : uint baseType; // Type, e.g. tournament, theory, etc.
74 : gamenumT numGames; // number of games in file.
75 : gamenumT autoLoad; // game number to autoload: 0=none, 1=1st, >numGames=last
76 : // description is a fixed-length string describing the database.
77 : char description [SCID_DESC_LENGTH + 1];
78 : // short description (8 chars) for the CUSTOM_FLAG_MAX bits for CUSTOM flags
79 : char customFlagDesc [CUSTOM_FLAG_MAX][CUSTOM_FLAG_DESC_LENGTH+1] ;
80 : bool dirty_; // If true, Header needs rewriting to disk.
81 : } Header;
82 :
83 : friend class CodecSCID4;
84 :
85 : public:
86 50 : Index() { Init(); }
87 : Index(const Index&);
88 : Index& operator=(const Index&);
89 50 : ~Index() { Clear(); }
90 :
91 : errorT Open(const char* filename, fileModeT fmode);
92 : errorT Create(const char* filename);
93 14 : errorT Close() { return Clear(); }
94 :
95 1126406 : const IndexEntry* GetEntry (gamenumT g) const {
96 1126406 : ASSERT(g < GetNumGames());
97 1105286 : return &(entries_[g]);
98 : }
99 :
100 : /**
101 : * GetBadNameIdCount() - return the number of invalid name handles.
102 : *
103 : * To save space, avoiding duplicates, the index keep handles
104 : * to strings stored in the namebase file.
105 : * If one of the two files is corrupted, the index may have
106 : * handles to strings that do not exists.
107 : * This functions returns the number of invalid name handles.
108 : */
109 0 : int GetBadNameIdCount() const { return nInvalidNameId_; }
110 :
111 : /**
112 : * Counts how many times every names contained in @e nb is used.
113 : * @param nb: the NameBase linked to this Index
114 : * @returns an array of std::vectors containing the count of each name.
115 : */
116 : std::array<std::vector<int>, NUM_NAME_TYPES>
117 12 : calcNameFreq(const NameBase& nb) const {
118 12 : std::array<std::vector<int>, NUM_NAME_TYPES> resVec;
119 60 : for (nameT n = NAME_PLAYER; n < NUM_NAME_TYPES; n++) {
120 48 : resVec[n].resize(nb.GetNumNames(n), 0);
121 : }
122 4012 : for (gamenumT i = 0, n = GetNumGames(); i < n; i++) {
123 4000 : const IndexEntry* ie = GetEntry(i);
124 4000 : resVec[NAME_PLAYER][ie->GetWhite()] += 1;
125 4000 : resVec[NAME_PLAYER][ie->GetBlack()] += 1;
126 4000 : resVec[NAME_EVENT][ie->GetEvent()] += 1;
127 4000 : resVec[NAME_SITE][ie->GetSite()] += 1;
128 4000 : resVec[NAME_ROUND][ie->GetRound()] += 1;
129 : }
130 12 : return resVec;
131 : }
132 :
133 : /**
134 : * Header getter functions
135 : */
136 1149330 : gamenumT GetNumGames () const { return Header.numGames; }
137 0 : uint GetType () const { return Header.baseType; }
138 0 : versionT GetVersion () const { return Header.version; }
139 0 : const char* GetDescription () const { return Header.description; }
140 0 : const char* GetCustomFlagDesc (byte c) const {
141 0 : if (c < IndexEntry::IDX_FLAG_CUSTOM1 || c > IndexEntry::IDX_FLAG_CUSTOM6) return 0;
142 0 : return Header.customFlagDesc[c - IndexEntry::IDX_FLAG_CUSTOM1];
143 : }
144 0 : gamenumT GetAutoLoad () const {
145 0 : return (Header.autoLoad <= Header.numGames) ? Header.autoLoad : Header.numGames;
146 : }
147 :
148 : /**
149 : * Header setter functions
150 : */
151 0 : errorT copyHeaderInfo(const Index& src) {
152 0 : if (fileMode_ == FMODE_ReadOnly) return ERROR_FileMode;
153 0 : Header.baseType = src.Header.baseType;
154 0 : Header.autoLoad = src.Header.autoLoad;
155 0 : std::memcpy(Header.description, src.Header.description, sizeof Header.description);
156 0 : std::memcpy(Header.customFlagDesc, src.Header.customFlagDesc, sizeof Header.customFlagDesc);
157 0 : Header.dirty_ = true;
158 0 : return flush();
159 : }
160 : errorT SetType (uint t) {
161 : if (fileMode_ == FMODE_ReadOnly) return ERROR_FileMode;
162 : Header.baseType = t;
163 : Header.dirty_ = true;
164 : return flush();
165 : }
166 : errorT SetDescription (const char* str) {
167 : if (fileMode_ == FMODE_ReadOnly) return ERROR_FileMode;
168 : strncpy(Header.description, str, SCID_DESC_LENGTH);
169 : Header.description[SCID_DESC_LENGTH] = 0;
170 : Header.dirty_ = true;
171 : return flush();
172 : }
173 : errorT SetCustomFlagDesc (byte c, const char* str) {
174 : if (fileMode_ == FMODE_ReadOnly) return ERROR_FileMode;
175 : if (c < IndexEntry::IDX_FLAG_CUSTOM1 || c > IndexEntry::IDX_FLAG_CUSTOM6) return ERROR_BadArg;
176 : char* flagDesc = Header.customFlagDesc[c - IndexEntry::IDX_FLAG_CUSTOM1];
177 : strncpy(flagDesc, str, CUSTOM_FLAG_DESC_LENGTH);
178 : flagDesc[CUSTOM_FLAG_DESC_LENGTH] = 0;
179 : Header.dirty_ = true;
180 : return flush();
181 : }
182 0 : errorT SetAutoLoad (gamenumT gnum) {
183 0 : if (fileMode_ == FMODE_ReadOnly) return ERROR_FileMode;
184 0 : Header.autoLoad = gnum;
185 0 : Header.dirty_ = true;
186 0 : return flush();
187 : }
188 :
189 : /**
190 : * FetchEntry() - return a modifiable pointer to a game's IndexEntry
191 : *
192 : * The pointer returned by this function allow to modify the IndexEntry
193 : * informations of a game. If modified, the IndexEntry object must be
194 : * passed to WriteEntry() to write the changes to the disks.
195 : * This functions is very error prone. For example:
196 : * IndexEntry* ie = FetchEntry(0);
197 : * ie->SetWhiteName(nb, "New player with white");
198 : * oops(); // the function oops() may call GetEntry(0) and get a messy object.
199 : * ie->SetBlackName(nb, "New player with black");
200 : *
201 : * A safer alternative is to create a temporary copy of the IndexEntry object
202 : * returned by GetEntry() and then write all the changes in a single step
203 : */
204 4339 : IndexEntry* FetchEntry (gamenumT g) {
205 4339 : ASSERT(g < GetNumGames());
206 4339 : return &(entries_[g]);
207 : }
208 :
209 : /**
210 : * WriteEntry() - modify a game in the Index
211 : */
212 : errorT WriteEntry(const IndexEntry* ie, gamenumT idx);
213 :
214 : /**
215 : * flush() - writes all cached data to the file
216 : */
217 84 : errorT flush() {
218 84 : if (FilePtr == 0) return OK;
219 19 : errorT errHeader = (Header.dirty_) ? WriteHeader() : OK;
220 19 : errorT errSync = (FilePtr->pubsync() != 0) ? ERROR_FileWrite : OK;
221 19 : return (errHeader == OK) ? errSync : errHeader;
222 : }
223 :
224 : private:
225 : void Init ();
226 : errorT Clear ();
227 : errorT WriteHeader ();
228 : };
229 :
230 :
231 : #endif // #ifdef SCID_INDEX_H
232 :
233 : //////////////////////////////////////////////////////////////////////
234 : // EOF: index.h
235 : //////////////////////////////////////////////////////////////////////
236 :
|