1 : #include <ept/popcon/popcon.h>
2 : #include <ept/popcon/maint/popconindexer.h>
3 : #include <ept/popcon/maint/path.h>
4 :
5 : #include <wibble/exception.h>
6 : #include <wibble/sys/fs.h>
7 :
8 : #include <tagcoll/diskindex/mmap.h>
9 :
10 : #include <set>
11 : #include <string>
12 : #include <cstdio>
13 : #include <cstring>
14 :
15 : using namespace std;
16 :
17 : namespace ept {
18 : namespace popcon {
19 :
20 : template<typename STRUCT>
21 : struct StructIndexer : public tagcoll::diskindex::MMapIndexer
22 1 : {
23 : const STRUCT& data;
24 1 : StructIndexer(const STRUCT& data) : data(data) {}
25 :
26 1 : int encodedSize() const { return sizeof(STRUCT); }
27 1 : void encode(char* buf) const { *(STRUCT*)buf = data; }
28 : };
29 :
30 : /// MMapIndexer that indexes the package names
31 : struct PopconGenerator : public tagcoll::diskindex::MMapIndexer
32 4 : {
33 : // Sorted set of all available package names and data
34 : std::map<std::string, Score> data;
35 :
36 1 : int encodedSize() const
37 : {
38 1 : int size = data.size() * sizeof(Score);
39 69911 : for (std::map<std::string, Score>::const_iterator i = data.begin();
40 : i != data.end(); ++i)
41 69910 : size += i->first.size() + 1;
42 1 : return tagcoll::diskindex::MMap::align(size);
43 : }
44 :
45 1 : void encode(char* buf) const
46 : {
47 1 : int pos = data.size() * sizeof(Score);
48 1 : int idx = 0;
49 69911 : for (std::map<std::string, Score>::const_iterator i = data.begin();
50 : i != data.end(); ++i)
51 : {
52 69910 : ((Score*)buf)[idx] = i->second;
53 69910 : ((Score*)buf)[idx].offset = pos;
54 69910 : memcpy(buf + pos, i->first.c_str(), i->first.size() + 1);
55 69910 : pos += i->first.size() + 1;
56 69910 : ++idx;
57 : }
58 1 : }
59 : };
60 :
61 :
62 7 : PopconIndexer::PopconIndexer()
63 : : mainSource(Path::popconSourceDir()),
64 7 : userSource(Path::popconUserSourceDir())
65 : {
66 7 : rescan();
67 7 : }
68 :
69 7 : void PopconIndexer::rescan()
70 : {
71 7 : ts_main_src = mainSource.timestamp();
72 7 : ts_user_src = userSource.timestamp();
73 7 : ts_main_sco = Path::timestamp(Path::scores());
74 14 : ts_user_sco = Path::timestamp(Path::userScores());
75 14 : ts_main_idx = Path::timestamp(Path::scoresIndex());
76 14 : ts_user_idx = Path::timestamp(Path::userScoresIndex());
77 7 : }
78 :
79 7 : bool PopconIndexer::needsRebuild() const
80 : {
81 : // If there are no indexes of any kind, then we need rebuilding
82 7 : if (ts_user_sco == 0 || ts_main_sco == 0 || ts_user_idx == 0 && ts_main_idx == 0)
83 2 : return true;
84 :
85 : // If the user index is ok, then we are fine
86 5 : if (ts_user_sco >= sourceTimestamp() && ts_user_idx >= sourceTimestamp())
87 5 : return false;
88 :
89 : // If there are user sources, then we cannot use the system index
90 0 : if (ts_user_src > 0)
91 0 : return true;
92 :
93 : // If there are no user sources, then we can fallback on the system
94 : // indexes in case the user indexes are not up to date
95 0 : if (ts_main_sco >= sourceTimestamp() && ts_main_idx >= sourceTimestamp())
96 0 : return false;
97 :
98 0 : return true;
99 : }
100 :
101 7 : bool PopconIndexer::userIndexIsRedundant() const
102 : {
103 : // If there is no user index, then it is not redundant
104 7 : if (ts_user_idx == 0)
105 1 : return false;
106 :
107 : // If the system index is not up to date, then the user index is not
108 : // redundant
109 6 : if (ts_main_idx < sourceTimestamp())
110 0 : return false;
111 :
112 6 : return true;
113 : }
114 :
115 2 : bool PopconIndexer::rebuild(const std::string& scofname, const std::string& idxfname)
116 : {
117 2 : PopconGenerator gen;
118 : InfoStruct is;
119 2 : is.submissions = 0;
120 2 : if (!mainSource.readScores(gen.data, is.submissions))
121 1 : userSource.readScores(gen.data, is.submissions);
122 2 : if (gen.data.empty())
123 1 : return false;
124 :
125 1 : StructIndexer<InfoStruct> infoStruct(is);
126 :
127 : // Create the index
128 1 : tagcoll::diskindex::MasterMMapIndexer master(idxfname);
129 1 : master.append(gen);
130 1 : master.append(infoStruct);
131 1 : master.commit();
132 :
133 : // for (map<string, Score>::const_iterator i = gen.data.begin(); i != gen.data.end(); ++i)
134 : // {
135 : // fprintf(stderr, "%s %d %f\n", i->first.c_str(), i->second.offset, i->second.score);
136 : // }
137 :
138 : // Create the score file
139 1 : FILE* out = fopen(scofname.c_str(), "wt");
140 1 : if (out == NULL)
141 0 : throw wibble::exception::File(scofname, "opening and truncating file for writing");
142 69911 : for (map<string, Score>::const_iterator i = gen.data.begin();
143 : i != gen.data.end(); ++i)
144 : {
145 69910 : fprintf(out, "%s %f\n", i->first.c_str(), i->second.score);
146 : }
147 1 : fclose(out);
148 1 : return true;
149 : }
150 :
151 7 : bool PopconIndexer::rebuildIfNeeded()
152 : {
153 7 : if (needsRebuild())
154 : {
155 : // Decide if we rebuild the user index or the system index
156 2 : if (Path::access(Path::popconIndexDir(), W_OK) == 0)
157 : {
158 : // Since we can write on the system index directory, we rebuild
159 : // the system index
160 2 : if (!rebuild(Path::scores(), Path::scoresIndex()))
161 1 : return false;
162 1 : ts_main_sco = Path::timestamp(Path::scores());
163 2 : ts_main_idx = Path::timestamp(Path::scoresIndex());
164 2 : if (Path::scores() == Path::userScores())
165 1 : ts_user_sco = ts_main_sco;
166 1 : if (Path::scoresIndex() == Path::userScoresIndex())
167 1 : ts_user_idx = ts_main_idx;
168 : } else {
169 0 : wibble::sys::fs::mkFilePath(Path::userScores());
170 0 : wibble::sys::fs::mkFilePath(Path::userScoresIndex());
171 0 : if (!rebuild(Path::userScores(), Path::userScoresIndex()))
172 0 : return false;
173 0 : ts_user_sco = Path::timestamp(Path::userScores());
174 0 : ts_user_idx = Path::timestamp(Path::userScoresIndex());
175 : }
176 1 : return true;
177 : }
178 5 : return false;
179 : }
180 :
181 7 : bool PopconIndexer::deleteRedundantUserIndex()
182 : {
183 7 : if (userIndexIsRedundant())
184 : {
185 : // Delete the user indexes if they exist
186 6 : if (Path::scores() != Path::userScores())
187 : {
188 0 : unlink(Path::userScores().c_str());
189 0 : ts_user_sco = 0;
190 : }
191 6 : if (Path::scoresIndex() != Path::userScoresIndex())
192 : {
193 0 : unlink(Path::userScoresIndex().c_str());
194 0 : ts_user_idx = 0;
195 : }
196 6 : return true;
197 : }
198 1 : return false;
199 : }
200 :
201 7 : bool PopconIndexer::getUpToDatePopcon(std::string& scofname, std::string& idxfname)
202 : {
203 : // If there are no indexes of any kind, then we have nothing to return
204 7 : if (ts_user_sco == 0 && ts_main_sco == 0 && ts_user_idx == 0 && ts_main_idx == 0)
205 1 : return false;
206 :
207 : // If the user index is up to date, use it
208 6 : if (ts_user_sco >= sourceTimestamp() &&
209 : ts_user_idx >= sourceTimestamp())
210 : {
211 6 : scofname = Path::userScores();
212 12 : idxfname = Path::userScoresIndex();
213 6 : return true;
214 : }
215 :
216 : // If the user index is not up to date and we have user sources, we cannot
217 : // fall back to the system index
218 0 : if (ts_user_src != 0)
219 0 : return false;
220 :
221 : // Fallback to the system index
222 0 : if (ts_main_sco >= sourceTimestamp() &&
223 : ts_main_idx >= sourceTimestamp())
224 : {
225 0 : scofname = Path::scores();
226 0 : idxfname = Path::scoresIndex();
227 0 : return true;
228 : }
229 :
230 0 : return false;
231 : }
232 :
233 :
234 7 : bool PopconIndexer::obtainWorkingPopcon(std::string& scofname, std::string& idxfname)
235 : {
236 7 : PopconIndexer indexer;
237 :
238 7 : indexer.rebuildIfNeeded();
239 7 : indexer.deleteRedundantUserIndex();
240 7 : return indexer.getUpToDatePopcon(scofname, idxfname);
241 : }
242 :
243 :
244 : }
245 6 : }
246 :
247 : // vim:set ts=4 sw=4:
|