1 : #include <ept/debtags/maint/debtagsindexer.h>
2 : #include <ept/debtags/maint/path.h>
3 : #include <ept/debtags/maint/pkgid.h>
4 : #include <ept/debtags/maint/serializer.h>
5 : #include <ept/debtags/vocabulary.h>
6 :
7 : #include <tagcoll/coll/intdiskindex.h>
8 : #include <tagcoll/coll/simple.h>
9 : #include <tagcoll/TextFormat.h>
10 : #include <tagcoll/stream/filters.h>
11 :
12 : #include <wibble/exception.h>
13 :
14 : #include <cstring>
15 :
16 : using namespace std;
17 :
18 : namespace ept {
19 : namespace debtags {
20 :
21 : /// MMapIndexer that indexes the package names
22 : struct PkgIdGenerator : public tagcoll::diskindex::MMapIndexer
23 2 : {
24 : // Sorted set of all available package names
25 : std::set<std::string> pkgs;
26 :
27 3 : int encodedSize() const
28 : {
29 3 : int size = pkgs.size() * sizeof(int);
30 63438 : for (std::set<std::string>::const_iterator i = pkgs.begin();
31 : i != pkgs.end(); ++i)
32 63435 : size += i->size() + 1;
33 3 : return tagcoll::diskindex::MMap::align(size);
34 : }
35 :
36 2 : void encode(char* buf) const
37 : {
38 2 : int pos = pkgs.size() * sizeof(int);
39 2 : int idx = 0;
40 42292 : for (std::set<std::string>::const_iterator i = pkgs.begin();
41 : i != pkgs.end(); ++i)
42 : {
43 42290 : ((int*)buf)[idx++] = pos;
44 42290 : memcpy(buf + pos, i->c_str(), i->size() + 1);
45 42290 : pos += i->size() + 1;
46 : }
47 2 : }
48 : };
49 :
50 :
51 10 : DebtagsIndexer::DebtagsIndexer(Vocabulary& voc)
52 : : voc(voc),
53 : mainSource(Path::debtagsSourceDir()),
54 10 : userSource(Path::debtagsUserSourceDir())
55 : {
56 10 : rescan();
57 10 : }
58 :
59 10 : void DebtagsIndexer::rescan()
60 : {
61 10 : ts_main_src = mainSource.timestamp();
62 10 : ts_user_src = userSource.timestamp();
63 10 : ts_main_tag = Path::timestamp(Path::tagdb());
64 20 : ts_main_idx = Path::timestamp(Path::tagdbIndex());
65 20 : ts_user_tag = Path::timestamp(Path::userTagdb());
66 20 : ts_user_idx = Path::timestamp(Path::userTagdbIndex());
67 10 : }
68 :
69 10 : bool DebtagsIndexer::needsRebuild() const
70 : {
71 : // If there are no indexes of any kind, then we need rebuilding
72 10 : if (ts_user_tag == 0 && ts_user_idx == 0 && ts_main_tag == 0 && ts_main_idx == 0)
73 2 : return true;
74 :
75 : // If the user index is ok, then we are fine
76 8 : if (ts_user_tag >= sourceTimestamp() && ts_user_idx >= sourceTimestamp())
77 8 : return false;
78 :
79 : // If there are user sources, then we cannot use the system index
80 0 : if (ts_user_src > 0)
81 0 : return true;
82 :
83 : // If there are no user sources, then we can fallback on the system
84 : // indexes in case the user indexes are not up to date
85 0 : if (ts_main_tag >= sourceTimestamp() && ts_main_idx >= sourceTimestamp())
86 0 : return false;
87 :
88 0 : return true;
89 : }
90 :
91 10 : bool DebtagsIndexer::userIndexIsRedundant() const
92 : {
93 : // If there is no user index, then it is not redundant
94 10 : if (ts_user_tag == 0 && ts_user_idx == 0)
95 1 : return false;
96 :
97 : // If we have user sources, then the user index is never redundant
98 9 : if (ts_user_src > 0)
99 9 : return false;
100 :
101 : // If the system index is not up to date, then the user index is not
102 : // redundant
103 0 : if (ts_main_tag < sourceTimestamp() || ts_main_idx < sourceTimestamp())
104 0 : return false;
105 :
106 0 : return true;
107 : }
108 :
109 2 : bool DebtagsIndexer::rebuild(const std::string& tagfname, const std::string& idxfname)
110 : {
111 : using namespace tagcoll;
112 :
113 2 : diskindex::MasterMMapIndexer master(idxfname);
114 :
115 : // Read and merge tag data
116 2 : coll::Simple<string, string> merged;
117 2 : mainSource.readTags(inserter(merged));
118 2 : userSource.readTags(inserter(merged));
119 :
120 2 : if (merged.empty())
121 : //throw wibble::exception::Consistency("Reading debtags sources from " + Path::debtagsSourceDir() + " and " + Path::debtagsUserSourceDir(), "Unable to find any tag data");
122 1 : return false;
123 :
124 : // Create the pkgid index
125 1 : PkgIdGenerator pkgidGen;
126 21146 : for (coll::Simple<string, string>::const_iterator i = merged.begin();
127 : i != merged.end(); ++i)
128 21145 : pkgidGen.pkgs.insert(i->first);
129 :
130 : // Temporary in-memory index to use for converting packages to ints while
131 : // creating the debtags index
132 1 : char buf[pkgidGen.encodedSize()];
133 1 : pkgidGen.encode(buf);
134 1 : PkgId pkgid(buf, pkgidGen.encodedSize());
135 :
136 : // Create the Debtags index
137 1 : coll::IntDiskIndexer tagindexer;
138 1 : merged.output(stringToInt(pkgid, voc, inserter(tagindexer)));
139 :
140 : // MMap 0: pkgid
141 1 : master.append(pkgidGen);
142 : // MMap 1: pkg->tag
143 1 : master.append(tagindexer.pkgIndexer());
144 : // MMap 2: tag->pkg
145 1 : master.append(tagindexer.tagIndexer());
146 :
147 : // Write the tag database in text format
148 1 : std::string tmpdb = tagfname + ".tmp";
149 1 : FILE* out = fopen(tmpdb.c_str(), "wt");
150 1 : if (!out) throw wibble::exception::File(tmpdb, "creating temporary copy of tag index");
151 1 : merged.output(textformat::StdioWriter(out));
152 1 : fclose(out);
153 :
154 : // Perform "atomic" update of the tag database
155 : // FIXME: cannot be atomic because race conditions happening between file
156 : // renames
157 1 : if (rename(tmpdb.c_str(), tagfname.c_str()) == -1)
158 0 : throw wibble::exception::System("Renaming " + tmpdb + " to " + tagfname);
159 :
160 1 : master.commit();
161 1 : return true;
162 : }
163 :
164 10 : bool DebtagsIndexer::rebuildIfNeeded()
165 : {
166 10 : if (needsRebuild())
167 : {
168 : // Decide if we rebuild the user index or the system index
169 :
170 2 : if (ts_user_src == 0 && Path::access(Path::debtagsIndexDir(), W_OK) == 0)
171 : {
172 : // There are no user sources and we can write to the system index
173 : // directory: rebuild the system index
174 1 : if (!rebuild(Path::tagdb(), Path::tagdbIndex()))
175 1 : return false;
176 0 : ts_main_tag = Path::timestamp(Path::tagdb());
177 0 : ts_main_idx = Path::timestamp(Path::tagdbIndex());
178 0 : if (Path::tagdb() == Path::userTagdb())
179 0 : ts_user_tag = ts_main_tag;
180 0 : if (Path::tagdbIndex() == Path::userTagdbIndex())
181 0 : ts_user_idx = ts_main_idx;
182 : } else {
183 1 : wibble::sys::fs::mkFilePath(Path::userTagdb());
184 2 : wibble::sys::fs::mkFilePath(Path::userTagdbIndex());
185 2 : if (!rebuild(Path::userTagdb(), Path::userTagdbIndex()))
186 0 : return false;
187 1 : ts_user_tag = Path::timestamp(Path::userTagdb());
188 2 : ts_user_idx = Path::timestamp(Path::userTagdbIndex());
189 : }
190 1 : return true;
191 : }
192 8 : return false;
193 : }
194 :
195 10 : bool DebtagsIndexer::deleteRedundantUserIndex()
196 : {
197 10 : if (userIndexIsRedundant())
198 : {
199 : // Delete the user indexes if they exist
200 0 : if (Path::tagdb() != Path::userTagdb())
201 : {
202 0 : unlink(Path::userTagdb().c_str());
203 0 : ts_user_tag = 0;
204 : }
205 0 : if (Path::tagdbIndex() != Path::userTagdbIndex())
206 : {
207 0 : unlink(Path::userTagdbIndex().c_str());
208 0 : ts_user_idx = 0;
209 : }
210 0 : return true;
211 : }
212 10 : return false;
213 : }
214 :
215 10 : bool DebtagsIndexer::getUpToDateTagdb(std::string& tagfname, std::string& idxfname)
216 : {
217 : // If there are no indexes of any kind, then we have nothing to return
218 10 : if (ts_user_tag == 0 && ts_user_idx == 0 && ts_main_tag == 0 && ts_main_idx == 0)
219 1 : return false;
220 :
221 : // If the user index is up to date, use it
222 9 : if (ts_user_tag >= sourceTimestamp() &&
223 : ts_user_idx >= sourceTimestamp())
224 : {
225 9 : tagfname = Path::userTagdb();
226 18 : idxfname = Path::userTagdbIndex();
227 9 : return true;
228 : }
229 :
230 : // If the user index is not up to date and we have user sources, we cannot
231 : // fall back to the system index
232 0 : if (ts_user_src != 0)
233 0 : return false;
234 :
235 : // Fallback to the system index
236 0 : if (ts_main_tag >= sourceTimestamp() &&
237 : ts_main_idx >= sourceTimestamp())
238 : {
239 0 : tagfname = Path::tagdb();
240 0 : idxfname = Path::tagdbIndex();
241 0 : return true;
242 : }
243 :
244 0 : return false;
245 : }
246 :
247 :
248 :
249 10 : bool DebtagsIndexer::obtainWorkingDebtags(Vocabulary& voc, std::string& tagfname, std::string& idxfname)
250 : {
251 10 : DebtagsIndexer t(voc);
252 :
253 10 : t.rebuildIfNeeded();
254 10 : t.deleteRedundantUserIndex();
255 10 : return t.getUpToDateTagdb(tagfname, idxfname);
256 : }
257 :
258 : }
259 : }
260 :
261 : #include <ept/debtags/maint/sourcedir.tcc>
262 : #include <tagcoll/coll/simple.tcc>
263 :
264 : // vim:set ts=4 sw=4:
265 : // -*- C++ -*-
|