Line data Source code
1 : /*
2 : * Copyright (C) 2016-2018 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 :
20 : /** @file
21 : * Implements the CodecProxy class, which serves as base class for non-native
22 : * databases.
23 : */
24 :
25 : #ifndef CODEC_PROXY_H
26 : #define CODEC_PROXY_H
27 :
28 : #include "codec.h"
29 : #include "codec_memory.h"
30 : #include "common.h"
31 : #include "game.h"
32 :
33 : #ifndef MULTITHREADING_OFF
34 : #include <atomic>
35 : #include <thread>
36 : #endif
37 :
38 : /**
39 : * Base class for non-native databases.
40 : * Every class derived from ICodecDatabase must keep an @e Index object and the
41 : * corresponding @e NameBase object fully updated in memory.
42 : * This implies that the virtual function dyn_open() must load in memory the
43 : * header's data of all the games; however a dependency between the codecs and
44 : * the @e Index class is not desirable.
45 : * This class provides an interface that encapsulates the codecs, requiring only
46 : * the ability to exchange @e Game objects.
47 : */
48 38 : template <typename Derived> class CodecProxy : public CodecMemory {
49 2036 : Derived* getDerived() { return static_cast<Derived*>(this); }
50 :
51 : public:
52 : /**
53 : * Opens/creates a database encoded in a non-native format.
54 : * @param filename: full path of the database to be opened.
55 : * @param fMode: valid file access mode.
56 : * @returns OK in case of success, an @p errorT code otherwise.
57 : */
58 : errorT open(const char* filename, fileModeT fMode);
59 :
60 : /**
61 : * Reads the next game.
62 : * A derived class implements this function to sequentially read the games
63 : * contained into the database.
64 : * @param Game&: the Game object where the data will be stored.
65 : * @returns
66 : * - ERROR_NotFound if there are no more games to be read.
67 : * - OK otherwise.
68 : */
69 : errorT parseNext(Game&) { return ERROR_NotFound; }
70 :
71 : /**
72 : * Returns info about the parsing progress.
73 : * @returns a pair<size_t, size_t> where first element is the quantity of
74 : * data parsed and second one is the total amount of data of the database.
75 : */
76 : std::pair<size_t, size_t> parseProgress() {
77 : return std::pair<size_t, size_t>(1, 1);
78 : }
79 :
80 : /**
81 : * Returns the list of errors produced by parseNext() calls.
82 : */
83 : const char* parseErrors() { return NULL; }
84 :
85 : /**
86 : * Adds a game into the database.
87 : * @param Game*: valid pointer to a Game object with the new data.
88 : * @returns OK in case of success, an @p errorT code otherwise.
89 : */
90 : errorT gameAdd(Game*) { return ERROR_CodecUnsupFeat; }
91 :
92 : /**
93 : * Replaces a game in the database.
94 : * @param Game*: valid pointer to a Game object with the new data.
95 : * @param gamenumT: valid gamenumT of the game to be replaced.
96 : * @returns OK in case of success, an @p errorT code otherwise.
97 : */
98 0 : errorT gameSave(Game*, gamenumT) { return ERROR_CodecUnsupFeat; }
99 :
100 : private:
101 1000 : errorT addGame(Game* game) final {
102 1000 : errorT err = getDerived()->gameAdd(game);
103 1000 : if (err != OK)
104 0 : return err;
105 :
106 1000 : return CodecMemory::addGame(game);
107 : }
108 :
109 0 : errorT saveGame(Game* game, gamenumT replaced) final {
110 0 : errorT err = getDerived()->gameSave(game, replaced);
111 0 : if (err != OK)
112 0 : return err;
113 :
114 0 : return CodecMemory::saveGame(game, replaced);
115 : }
116 :
117 1000 : errorT addGame(const IndexEntry* srcIe, const NameBase* srcNb,
118 : const byte* srcData, size_t dataLen) final {
119 2000 : ByteBuffer buf(0);
120 1000 : buf.ProvideExternal(const_cast<byte*>(srcData), dataLen);
121 2000 : Game game;
122 1000 : errorT err = game.Decode(&buf, GAME_DECODE_ALL);
123 1000 : if (err == OK)
124 1000 : err = game.LoadStandardTags(srcIe, srcNb);
125 1000 : if (err != OK)
126 0 : return err;
127 :
128 1000 : err = getDerived()->gameAdd(&game);
129 1000 : if (err != OK)
130 0 : return err;
131 :
132 1000 : return CodecMemory::addGame(srcIe, srcNb, srcData, dataLen);
133 : }
134 :
135 0 : errorT saveIndexEntry(const IndexEntry&, gamenumT) final {
136 0 : return ERROR_CodecUnsupFeat;
137 : }
138 :
139 0 : std::pair<errorT, idNumberT> addName(nameT, const char*) final {
140 0 : return std::pair<errorT, idNumberT>(ERROR_CodecUnsupFeat, 0);
141 : }
142 :
143 : /*
144 : * Create a memory database, open the non-native database @p filename and
145 : * copy all the games into the memory database.
146 : */
147 19 : errorT dyn_open(fileModeT fMode, const char* filename,
148 : const Progress& progress, Index* idx, NameBase* nb) final {
149 19 : if (filename == 0)
150 0 : return ERROR;
151 :
152 19 : errorT err = CodecMemory::dyn_open(FMODE_Memory, filename, progress,
153 : idx, nb);
154 19 : if (err != OK)
155 0 : return err;
156 :
157 19 : err = getDerived()->open(filename, fMode);
158 19 : if (err != OK)
159 2 : return err;
160 :
161 2023 : return parseGames(progress, *getDerived(), [&](Game& game) {
162 2006 : return this->CodecMemory::addGame(&game);
163 2023 : });
164 : }
165 :
166 : public:
167 : /*
168 : * Given a source database of type CodecProxy<T>, for each game a
169 : * corresponding Game object is created and dispatched to @e destFn.
170 : */
171 : template <typename TProgress, typename TSource, typename TDestFn>
172 17 : static errorT parseGames(const TProgress& progress, TSource& src,
173 : TDestFn destFn) {
174 : #ifndef MULTITHREADING_OFF
175 17 : auto workTotal = src.parseProgress().second;
176 :
177 34 : Game game[4];
178 17 : std::atomic<size_t> workDone{};
179 17 : std::atomic<int8_t> sync[4] = {};
180 : enum {sy_free, sy_used, sy_stop};
181 :
182 51 : std::thread producer([&]() {
183 : uint64_t slot;
184 17 : uint64_t nProduced = 0;
185 2006 : while (true) {
186 2023 : slot = nProduced % 4;
187 : int sy;
188 : while (true) { // spinlock if the slot is in use
189 6090 : sy = sync[slot].load(std::memory_order_acquire);
190 2030 : if (sy == sy_used)
191 7 : std::this_thread::yield();
192 : else
193 2023 : break;
194 : };
195 2023 : if (sy == sy_stop)
196 0 : break;
197 :
198 2023 : if (src.parseNext(game[slot]) == ERROR_NotFound)
199 17 : break;
200 :
201 2006 : if (++nProduced % 1024 == 0) {
202 0 : workDone.store(src.parseProgress().first,
203 : std::memory_order_release);
204 : }
205 :
206 2006 : sync[slot].store(sy_used, std::memory_order_release);
207 : }
208 17 : sync[slot].store(sy_stop, std::memory_order_release);
209 17 : });
210 :
211 : // Consumer
212 17 : errorT err = OK;
213 : uint64_t slot;
214 17 : uint64_t nImported = 0;
215 2006 : while (true) {
216 2023 : slot = nImported % 4;
217 : int sy;
218 : while (true) { // spinlock if the slot is empty
219 27262862 : sy = sync[slot].load(std::memory_order_acquire);
220 9088295 : if (sy == sy_free)
221 9086272 : std::this_thread::yield();
222 : else
223 2023 : break;
224 : };
225 2023 : if (sy == sy_stop)
226 17 : break;
227 :
228 2006 : if (++nImported % 1024 == 0) {
229 0 : if (!progress.report(workDone.load(std::memory_order_acquire),
230 : workTotal)) {
231 0 : err = ERROR_UserCancel;
232 0 : break;
233 : }
234 : }
235 :
236 2006 : err = destFn(game[slot]);
237 2006 : if (err != OK)
238 0 : break;
239 :
240 2006 : sync[slot].store(sy_free, std::memory_order_release);
241 : }
242 17 : sync[slot].store(sy_stop, std::memory_order_release);
243 :
244 17 : producer.join();
245 17 : progress(1, 1, src.parseErrors());
246 34 : return err;
247 :
248 : #else
249 : Game g;
250 : errorT err = OK;
251 : uint64_t nImported = 0;
252 : while (src.parseNext(g) != ERROR_NotFound) {
253 : err = destFn(g);
254 : if (err != OK)
255 : break;
256 :
257 : if (++nImported % 1024 == 0) {
258 : std::pair<size_t, size_t> count = src.parseProgress();
259 : if (!progress.report(count.first, count.second)) {
260 : err = ERROR_UserCancel;
261 : break;
262 : }
263 : }
264 : }
265 : progress(1, 1, src.parseErrors());
266 : return err;
267 : #endif
268 : }
269 : };
270 :
271 : #endif
|