1 /*
2  * Copyright (C) 2011  Gerd Lorscheid
3  * Copyright (C) 2011-2017  Fulvio Benini
4  *
5  * This file is part of Scid (Shane's Chess Information Database).
6  *
7  * Scid is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation.
10  *
11  * Scid is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Scid.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 /** @file
21  * Defines the SortCache class, which sorts the games of an @e Index.
22  */
23 
24 #ifndef SORTCACHE_H
25 #define SORTCACHE_H
26 
27 #include "common.h"
28 
29 #ifndef MULTITHREADING_OFF
30 #include <atomic>
31 using std::atomic_bool;
32 #else
33 typedef bool atomic_bool;
34 #endif
35 
36 class HFilter;
37 class Index;
38 class IndexEntry;
39 class NameBase;
40 
41 /**
42  * This class sorts games contained into an Index.
43  * Multiple SortCache objects can be created for a single Index, allowing to
44  * simultaneously sort the games by multiple criteria in an independent way.
45  */
46 class SortCache {
47 	gamenumT nGames_;
48 	atomic_bool valid_fullMap_;
49 	atomic_bool th_interrupt_;
50 	bool partialHash_;
51 	gamenumT* fullMap_;
52 	void* th_;
53 	uint32_t* hash_;
54 	const Index* index_;
55 	const NameBase* nbase_;
56 	char criteria_[32];
57 	int refCount_;
58 
59 	// Valid fields that can be used to sort the games.
60 	enum {
61 		SORTING_date =         'd',
62 		SORTING_year =         'y',
63 		SORTING_event =        'e',
64 		SORTING_site =         's',
65 		SORTING_round =        'n',
66 		SORTING_white =        'w',
67 		SORTING_black =        'b',
68 		SORTING_eco =          'o',
69 		SORTING_result =       'r',
70 		SORTING_moveCount =    'm',
71 		SORTING_avgElo =       'R',
72 		SORTING_country =      'c',
73 		SORTING_deleted =      'D',
74 		SORTING_eventdate =    'E',
75 		SORTING_whiteelo =     'W',
76 		SORTING_blackelo =     'B',
77 		SORTING_commentcount = 'C',
78 		SORTING_varcount =     'V',
79 		SORTING_nagcount =     'A',
80 		SORTING_resultwin =    '1',
81 		SORTING_resultdraw =   '5',
82 		SORTING_resultloss =   '0',
83 		SORTING_rating =       'i',
84 		SORTING_number =       'N',
85 		SORTING_sentinel =    '\0'
86 	};
87 
88 public:
89 	/**
90 	 * Create a new SortCache object, builds the hash table, and asynchronously
91 	 * sorts all the games.
92 	 * @param idx:      valid pointer to an Index object, witch contains the
93 	 *                  header data of the games to be sorted.
94 	 * @param nb:       valid pointer to the NameBase corresponding to @e idx.
95 	 * @param criteria: the list of fields by which games will be ordered.
96 	 *                  Each field should be followed by '+' to indicate an
97 	 *                  ascending order or by '-' for a descending order.
98 	 * @returns a pointer to the new object in case of success, NULL otherwise.
99 	 */
100 	static SortCache* create(const Index* idx, const NameBase* nb,
101 	                         const char* criteria);
102 	~SortCache();
103 
104 	/**
105 	 * Notify the object that a game's header data has changed.
106 	 * @param gameId: the id of the game whose data has been changed.
107 	 */
108 	void checkForChanges(gamenumT gameId);
109 
110 	/**
111 	 * Interrupt any asynchronous operation. This function must be called before
112 	 * modifying the Index or the NameBase associated with the SortCache.
113 	 */
prepareForChanges()114 	void prepareForChanges() { th_interrupt(); }
115 
116 	/**
117 	 * Retrieve the sorted list of games' ids.
118 	 * The behavior of this function is similar to the mySQL statement:
119 	 *     SELECT gameId FROM idx WHERE filter(gameId) != 0 ORDER BY criteria
120 	 *     LIMIT offset, row_count
121 	 * @param row_offset:  the offset of the first row to return.
122 	 *                     The offset of the initial row is 0.
123 	 * @param row_count:   maximum number of rows to return.
124 	 * @param filter:      a reference to a valid (!= NULL) HFilter object.
125 	 *                     Games not included into the filter will be ignored.
126 	 * @param[out] result: valid pointer to an array where the sorted list of
127 	 *                     games will be stored (should be able to contain at
128 	 *                     least @e row_count elements).
129 	 * @returns the number of games' ids stored into @e result.
130 	 */
131 	size_t select(size_t row_offset, size_t row_count, const HFilter& filter,
132 	              gamenumT* result) const;
133 
134 	/**
135 	 * Get the sorted position of a game.
136 	 * @param gameId: the id of the game.
137 	 * @param filter: a reference to a valid (!= NULL) HFilter object.
138 	 *                Games not included into the filter will be ignored,
139 	 *                and @e gameId must be included into the filter.
140 	 * @returns the sorted position of @e gameId.
141 	 */
142 	size_t sortedPosition(gamenumT gameId, const HFilter& filter) const;
143 
incrRef(int incr)144 	int incrRef(int incr) { return refCount_ += incr; }
145 
146 private:
147 	SortCache(const Index* idx, const NameBase* nbase);
148 	SortCache(const SortCache&);
149 
150 	class CmpLess {
151 		const SortCache* sc_;
152 	public:
CmpLess(const SortCache * sc)153 		CmpLess(const SortCache* sc) : sc_(sc) {}
154 		bool operator()(gamenumT g1, gamenumT g2) const;
155 	};
156 	int fullCompare(gamenumT left, gamenumT right) const;
157 
158 	uint32_t calcHash(gamenumT gameId);
159 	void generateHashCache();
160 	void sortAsynchronously();
161 
th_interrupt()162 	void th_interrupt() {
163 		th_interrupt_ = true;
164 		th_join();
165 		th_interrupt_ = false;
166 	}
167 	void th_join();
168 	void th_sort();
169 };
170 
171 #endif
172