1 /***************************************************************************
2 * (C) 2005-2006 Marius Roets <roets.marius@gmail.com> *
3 * (C) 2007 Rico Zenklusen <rico_z@users.sourceforge.net> *
4 * (C) 2007-2009 Michal Rudolf <mrudolf@kdewebdev.org> *
5 * *
6 * This program 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; either version 2 of the License, or *
9 * (at your option) any later version. *
10 ***************************************************************************/
11
12 #include <QtDebug>
13 #include <QFile>
14 #include <QDataStream>
15 #include <QHash>
16 #include <QMultiHash>
17 #include <QWriteLocker>
18 #include <QReadLocker>
19 #include <QVector>
20
21 #include "index.h"
22 #include "tags.h"
23
24 using namespace chessx;
25
26 #if defined(_MSC_VER) && defined(_DEBUG)
27 #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
28 #define new DEBUG_NEW
29 #endif // _MSC_VER
30
IndexX()31 IndexX::IndexX() : m_mutex(QReadWriteLock::Recursive)
32 {
33 // Dummy Values in case a index is miscalculated
34 init();
35 }
36
~IndexX()37 IndexX::~IndexX()
38 {
39 }
40
add()41 GameId IndexX::add()
42 {
43 QWriteLocker m(&m_mutex);
44 GameId gameId = m_indexItems.count();
45 m_indexItems.insert(gameId, IndexItem());
46 return gameId;
47 }
48
AddTagName(QString name)49 TagIndex IndexX::AddTagName(QString name)
50 {
51 if(m_tagNameIndex.contains(name))
52 {
53 return m_tagNameIndex.value(name);
54 }
55 TagIndex n = m_tagNameIndex.size();
56 m_tagNameIndex[name] = n;
57 m_tagNames[n] = name;
58 return n;
59 }
60
AddTagValue(QString name)61 ValueIndex IndexX::AddTagValue(QString name)
62 {
63 ValueIndex n = qHash(name);
64 if (m_tagValues.contains(n))
65 {
66 if (m_tagValues[n] == name)
67 {
68 return n;
69 }
70 name.append(QChar(0));
71 QString prelim = name;
72 int i = 0;
73 do {
74 prelim = name + QString::number(i++);
75 n = qHash(prelim);
76 if (m_tagValues.contains(n))
77 {
78 if (m_tagValues[n] == prelim)
79 {
80 return n;
81 }
82 }
83 } while(m_tagValues.contains(n));
84 name = prelim;
85 }
86 m_tagValues[n] = name;
87 return n;
88 }
89
setTag(const QString & tagName,const QString & value,GameId gameId)90 void IndexX::setTag(const QString& tagName, const QString& value, GameId gameId)
91 {
92 QWriteLocker m(&m_mutex); // PERF 10s aus 30s (aus 115s Gesamtdatei)
93 setTag_nolock(tagName, value, gameId);
94 }
95
setTag_nolock(const QString & tagName,const QString & value,GameId gameId)96 void IndexX::setTag_nolock(const QString& tagName, const QString& value, GameId gameId)
97 {
98 TagIndex tagIndex = AddTagName(tagName);
99 ValueIndex valueIndex = AddTagValue(value);
100
101 if (m_indexItems.count() <= (int)gameId)
102 {
103 (void) add();
104 }
105 m_indexItems[gameId].set(tagIndex, valueIndex);
106 }
107
removeTag(const QString & tagName,GameId gameId)108 void IndexX::removeTag(const QString& tagName, GameId gameId)
109 {
110 QWriteLocker m(&m_mutex);
111 if(m_tagNameIndex.contains(tagName))
112 {
113 TagIndex tagIndex = m_tagNameIndex.value(tagName);
114 if((int)gameId < m_indexItems.count())
115 {
116 m_indexItems[gameId].remove(tagIndex);
117 }
118 }
119 }
120
setValidFlag(GameId gameId,bool value)121 void IndexX::setValidFlag(GameId gameId, bool value)
122 {
123 if (!value)
124 {
125 m_validFlags.insert(gameId);
126 }
127 else
128 {
129 m_validFlags.remove(gameId);
130 }
131 }
132
replaceTagValue(QStringList tags,const QString & newValue,const QString & oldValue)133 bool IndexX::replaceTagValue(QStringList tags, const QString& newValue, const QString& oldValue)
134 {
135 QWriteLocker m(&m_mutex);
136
137 bool ok = false;
138 foreach(QString t, tags)
139 {
140 if(m_tagNameIndex.contains(t))
141 {
142 ok = true;
143 break;
144 }
145 }
146 ValueIndex valueIndex = getValueIndex(oldValue);
147 if(!ok || !m_tagValues.contains(valueIndex))
148 {
149 return false;
150 }
151 ValueIndex newIndex = getValueIndex(newValue);
152
153 if (!m_tagValues.contains(newIndex))
154 {
155 (void) AddTagValue(newValue); // Adds newIndex-newValue pair, return must be newIndex
156 }
157
158 QList<TagIndex> tl;
159 foreach (QString t, tags)
160 {
161 tl << getTagIndex(t);
162 }
163
164 QVector<IndexItem>::iterator i;
165 for (i = m_indexItems.begin(); i != m_indexItems.end(); ++i)
166 {
167 i->replaceValue(tl, valueIndex, newIndex);
168 }
169
170 m_tagValues.remove(valueIndex);
171 return true;
172 }
173
isValidFlag(GameId gameId) const174 bool IndexX::isValidFlag(GameId gameId) const
175 {
176 QReadLocker m(&m_mutex);
177 return !m_validFlags.contains(gameId);
178 }
179
write(QDataStream & out) const180 bool IndexX::write(QDataStream &out) const
181 {
182 QReadLocker m(&m_mutex);
183
184 out << m_tagNames;
185 out << m_tagValues;
186 out << m_indexItems;
187 out << m_validFlags;
188
189 bool extension = false;
190 out << extension;
191
192 return true;
193 }
194
reserve(quint32 estimation)195 void IndexX::reserve(quint32 estimation)
196 {
197 m_tagValues.reserve(estimation+16);
198 qDebug() << "Index space " << m_tagValues.capacity();
199 }
200
squeeze()201 void IndexX::squeeze()
202 {
203 qDebug() << "Index space " << m_tagValues.capacity();
204 m_tagValues.squeeze();
205 qDebug() << "Index space " << m_tagValues.capacity();
206 }
207
read(QDataStream & in,volatile bool * breakFlag,short version)208 bool IndexX::read(QDataStream &in, volatile bool *breakFlag, short version)
209 {
210 Q_UNUSED(version);
211
212 QWriteLocker m(&m_mutex);
213
214 in >> m_tagNames;
215 in >> m_tagValues;
216 in >> m_indexItems;
217 in >> m_validFlags;
218
219 bool extension;
220 in >> extension;
221
222 m_tagNameIndex.clear();
223
224 calculateCache(breakFlag);
225
226 return !(*breakFlag);
227 }
228
clearCache()229 void IndexX::clearCache()
230 {
231 QWriteLocker m(&m_mutex);
232 m_tagNameIndex.clear();
233 }
234
calculateCache(volatile bool * breakFlag)235 void IndexX::calculateCache(volatile bool* breakFlag)
236 {
237 calculateReverseMaps(breakFlag);
238 }
239
calculateReverseMaps(volatile bool * breakFlag)240 void IndexX::calculateReverseMaps(volatile bool* breakFlag)
241 {
242 if(m_tagNameIndex.isEmpty())
243 {
244 for (auto it = m_tagNames.cbegin(); it != m_tagNames.cend(); ++it)
245 {
246 if(breakFlag && *breakFlag)
247 {
248 return;
249 }
250 m_tagNameIndex.insert(it.value(), it.key());
251 }
252 }
253 }
254
init()255 void IndexX::init()
256 {
257 AddTagName("?");
258 AddTagValue("?");
259 }
260
clear()261 void IndexX::clear()
262 {
263 QWriteLocker m(&m_mutex);
264 m_indexItems.clear();
265 m_tagNames.clear();
266 m_tagNameIndex.clear();
267 m_tagValues.clear();
268 m_deletedGames.clear();
269 m_validFlags.clear();
270 init(); // Just to make sure that the index can be used after clearing
271 }
272
count() const273 int IndexX::count() const
274 {
275 return m_indexItems.count();
276 }
277
listInSet(const QString & tagName,const QSet<QString> & set) const278 QBitArray IndexX::listInSet(const QString& tagName, const QSet<QString>& set) const
279 {
280 QReadLocker m(&m_mutex);
281
282 TagIndex tagIndex = m_tagNameIndex.value(tagName);
283
284 QBitArray list(count(), false);
285 for(int i = 0; i < count(); ++i)
286 {
287 QString value = tagValue(tagIndex, i);
288 bool b = false;
289 foreach(QString s, set)
290 {
291 if (value.contains(s, Qt::CaseInsensitive))
292 {
293 b = true;
294 break;
295 }
296 }
297 list.setBit(i, b);
298 }
299 return list;
300 }
301
listInRange(const QString & tagName,const QString & minValue,const QString & maxValue) const302 QBitArray IndexX::listInRange(const QString& tagName, const QString& minValue, const QString& maxValue) const
303 {
304 QReadLocker m(&m_mutex);
305
306 TagIndex tagIndex = m_tagNameIndex.value(tagName);
307
308 QBitArray list(count(), false);
309 for(int i = 0; i < count(); ++i)
310 {
311 QString value = tagValue(tagIndex, i);
312 list.setBit(i, (minValue <= value) && (value <= maxValue));
313 }
314 return list;
315 }
316
listInRange(const QString & tagName,int minValue,int maxValue) const317 QBitArray IndexX::listInRange(const QString &tagName, int minValue, int maxValue) const
318 {
319 QReadLocker m(&m_mutex);
320
321 TagIndex tagIndex = m_tagNameIndex.value(tagName);
322
323 QBitArray list(count(), false);
324 for(int i = 0; i < count(); ++i)
325 {
326 int value = tagValue(tagIndex, i).toInt();
327 list.setBit(i, (minValue <= value) && (value <= maxValue));
328 }
329 return list;
330 }
331
listPartialValue(const QString & tagName,QString value) const332 QBitArray IndexX::listPartialValue(const QString& tagName, QString value) const
333 {
334 QReadLocker m(&m_mutex);
335 value.replace("-","\\-"); // Avoid - to become range
336 value.replace("(","\\("); // Avoid () to become regex
337 value.replace(")","\\)");
338 TagIndex tagIndex = m_tagNameIndex.value(tagName);
339 QRegExp re(value);
340 re.setCaseSensitivity(Qt::CaseInsensitive);
341 QBitArray list(count(), false);
342 for(int i = 0; i < count(); ++i)
343 {
344 QString gameValue = tagValue(tagIndex, i);
345 list.setBit(i, gameValue.contains(re));
346 }
347 return list;
348 }
349
tagValue_byIndex(TagIndex tagIndex,GameId gameId) const350 QString IndexX::tagValue_byIndex(TagIndex tagIndex, GameId gameId) const
351 {
352 QReadLocker m(&m_mutex);
353
354 ValueIndex valueIndex = m_indexItems[gameId].valueIndex(tagIndex);
355
356 return tagValueName(valueIndex);
357 }
358
tagValue(TagIndex tagIndex,GameId gameId) const359 QString IndexX::tagValue(TagIndex tagIndex, GameId gameId) const
360 {
361 ValueIndex valueIndex = m_indexItems[gameId].valueIndex(tagIndex);
362
363 return tagValueName(valueIndex);
364 }
365
tagName(TagIndex tagIndex) const366 QString IndexX::tagName(TagIndex tagIndex) const
367 {
368 return m_tagNames.value(tagIndex);
369 }
370
tagValueName(ValueIndex valueIndex) const371 QString IndexX::tagValueName(ValueIndex valueIndex) const
372 {
373 QString r = m_tagValues.value(valueIndex);
374 return r.section(QChar(0),0,0);
375 }
376
tagValue(const QString & tagName,GameId gameId) const377 QString IndexX::tagValue(const QString& tagName, GameId gameId) const
378 {
379 QReadLocker m(&m_mutex);
380 TagIndex tagIndex = m_tagNameIndex.value(tagName);
381 return tagValue(tagIndex, gameId);
382 }
383
valueIndexFromTag(const QString & tagName,GameId gameId) const384 ValueIndex IndexX::valueIndexFromTag(const QString& tagName, GameId gameId) const
385 {
386 QReadLocker m(&m_mutex);
387 TagIndex tagIndex = m_tagNameIndex.value(tagName);
388 return valueIndexFromIndex(tagIndex, gameId);
389 }
390
indexItemHasTag(TagIndex tagIndex,GameId gameId) const391 bool IndexX::indexItemHasTag(TagIndex tagIndex, GameId gameId) const
392 {
393 return m_indexItems[gameId].hasTagIndex(tagIndex);
394 }
395
valueIndexFromIndex(TagIndex tagIndex,GameId gameId) const396 inline ValueIndex IndexX::valueIndexFromIndex(TagIndex tagIndex, GameId gameId) const
397 {
398 return m_indexItems[gameId].valueIndex(tagIndex);
399 }
400
getTagIndex(const QString & value) const401 TagIndex IndexX::getTagIndex(const QString& value) const
402 {
403 if(m_tagNameIndex.contains(value))
404 {
405 return m_tagNameIndex.value(value);
406 }
407 return TagNoIndex;
408 }
409
getValueIndex(QString name) const410 ValueIndex IndexX::getValueIndex(QString name) const
411 {
412 ValueIndex n = qHash(name);
413
414 if (m_tagValues.contains(n))
415 {
416 if (m_tagValues[n] == name)
417 {
418 return n;
419 }
420 name.append(QChar(0));
421 QString prelim;
422 int i = 0;
423 do {
424 prelim = name + QString::number(i++);
425 n = qHash(prelim);
426 if (m_tagValues.contains(n))
427 {
428 if (m_tagValues[n] == prelim)
429 {
430 return n;
431 }
432 }
433 } while(m_tagValues.contains(n));
434 }
435
436 return n;
437 }
438
hashIndexItem(GameId gameId) const439 unsigned int IndexX::hashIndexItem(GameId gameId) const
440 {
441 return valueIndexFromTag(TagNameWhite, gameId);
442 }
443
isIndexItemEqual(GameId i,GameId j) const444 bool IndexX::isIndexItemEqual(GameId i, GameId j) const
445 {
446 QReadLocker m(&m_mutex);
447 const IndexItem& iItem = m_indexItems.value(i);
448 const IndexItem& jItem = m_indexItems.value(j);
449 return (iItem.isEqual(jItem));
450 }
451
loadGameHeaders(GameId id,GameX & game) const452 void IndexX::loadGameHeaders(GameId id, GameX& game) const
453 {
454 QReadLocker m(&m_mutex);
455
456 game.clearTags();
457 foreach(TagIndex tagIndex, m_indexItems[id].getTagIndices())
458 {
459 // qDebug() << "lGH>" << &game << " " << id << " " << tagName(tagIndex) << " " << tagValue(tagIndex, id);
460 game.setTag(tagName(tagIndex), tagValue(tagIndex, id));
461 }
462 }
463
loadGameHeader(GameId id,GameX & game,const QString & tag) const464 void IndexX::loadGameHeader(GameId id, GameX& game, const QString &tag) const
465 {
466 QReadLocker m(&m_mutex);
467
468 TagIndex tagIndex = getTagIndex(tag);
469 game.setTag(tagName(tagIndex), tagValue(tagIndex, id));
470 }
471
playerNames() const472 QStringList IndexX::playerNames() const
473 {
474 QReadLocker m(&m_mutex);
475
476 QStringList allPlayerNames;
477 QSet<ValueIndex> playerNameIndex;
478
479 TagIndex tagIndex = getTagIndex(TagNameWhite);
480 if(tagIndex != TagNoIndex)
481 {
482 QVector<IndexItem>::const_iterator i;
483 for (i = m_indexItems.constBegin(); i != m_indexItems.constEnd(); ++i)
484 {
485 playerNameIndex.insert(i->valueIndex(tagIndex));
486 }
487 }
488
489 tagIndex = getTagIndex(TagNameBlack);
490 if(tagIndex != TagNoIndex)
491 {
492 QVector<IndexItem>::const_iterator i;
493 for (i = m_indexItems.constBegin(); i != m_indexItems.constEnd(); ++i)
494 {
495 playerNameIndex.insert(i->valueIndex(tagIndex));
496 }
497 }
498
499 foreach(ValueIndex valueIndex, playerNameIndex)
500 {
501 allPlayerNames.append(tagValueName(valueIndex));
502 }
503
504 return allPlayerNames;
505 }
506
tagValueSet(const QString & tagName) const507 QSet<ValueIndex> IndexX::tagValueSet(const QString& tagName) const
508 {
509 QReadLocker m(&m_mutex);
510
511 QSet<ValueIndex> tagNameIndex;
512 TagIndex tagIndex = getTagIndex(tagName);
513
514 if (tagIndex != TagNoIndex)
515 {
516 QVector<IndexItem>::const_iterator i;
517 for (i = m_indexItems.constBegin(); i != m_indexItems.constEnd(); ++i)
518 {
519 tagNameIndex.insert(i->valueIndex(tagIndex));
520 }
521 }
522 return tagNameIndex;
523 }
524
tagValues(const QString & tagName) const525 QStringList IndexX::tagValues(const QString& tagName) const
526 {
527 QStringList allTagNames;
528 QSet<ValueIndex> set = tagValueSet(tagName);
529 foreach(ValueIndex valueIndex, set)
530 {
531 allTagNames.append(tagValueName(valueIndex));
532 }
533 return allTagNames;
534 }
535
deleted(GameId gameId) const536 bool IndexX::deleted(GameId gameId) const
537 {
538 return m_deletedGames.contains(gameId);
539 }
540
setDeleted(GameId gameId,bool df)541 void IndexX::setDeleted(GameId gameId, bool df)
542 {
543 if (df)
544 {
545 m_deletedGames.insert(gameId);
546 }
547 else
548 {
549 m_deletedGames.remove(gameId);
550 }
551 }
552