Branch data Line data Source code
1 : : /** @file flint_synonym.cc
2 : : * @brief Synonym data for a flint database.
3 : : */
4 : : /* Copyright (C) 2004,2005,2006,2007,2008,2009 Olly Betts
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 : : * This program 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 this program; if not, write to the Free Software
18 : : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 : : */
20 : :
21 : : #include <config.h>
22 : : #include "flint_synonym.h"
23 : :
24 : : #include "xapian/error.h"
25 : :
26 : : #include "debuglog.h"
27 : : #include "flint_cursor.h"
28 : : #include "flint_utils.h"
29 : : #include "stringutils.h"
30 : : #include "vectortermlist.h"
31 : :
32 : : #include <set>
33 : : #include <string>
34 : : #include <vector>
35 : :
36 : : using namespace std;
37 : :
38 : : // We XOR the length values with this so that they are more likely to coincide
39 : : // with lower case ASCII letters, which are likely to be common. This means
40 : : // that zlib should do a better job of compressing tag values.
41 : : #define MAGIC_XOR_VALUE 96
42 : :
43 : : void
44 : 563 : FlintSynonymTable::merge_changes()
45 : : {
46 [ + + ]: 563 : if (last_term.empty()) return;
47 : :
48 [ + + ]: 93 : if (last_synonyms.empty()) {
49 : 1 : del(last_term);
50 : : } else {
51 : 92 : string tag;
52 : :
53 : 92 : set<string>::const_iterator i;
54 [ + + ]: 187 : for (i = last_synonyms.begin(); i != last_synonyms.end(); ++i) {
55 : 95 : const string & synonym = *i;
56 : 95 : tag += byte(synonym.size() ^ MAGIC_XOR_VALUE);
57 : 95 : tag += synonym;
58 : : }
59 : :
60 : 92 : add(last_term, tag);
61 : 92 : last_synonyms.clear();
62 : : }
63 : 563 : last_term.resize(0);
64 : : }
65 : :
66 : : void
67 : 94 : FlintSynonymTable::add_synonym(const string & term, const string & synonym)
68 : : {
69 [ + + ]: 94 : if (last_term != term) {
70 : 91 : merge_changes();
71 : 91 : last_term = term;
72 : :
73 : 91 : string tag;
74 [ - + ]: 91 : if (get_exact_entry(term, tag)) {
75 : 0 : const char * p = tag.data();
76 : 0 : const char * end = p + tag.size();
77 [ # # ]: 0 : while (p != end) {
78 : : size_t len;
79 [ # # ][ # # ]: 0 : if (p == end ||
[ # # ]
80 : : (len = byte(*p) ^ MAGIC_XOR_VALUE) >= size_t(end - p))
81 : 0 : throw Xapian::DatabaseCorruptError("Bad synonym data");
82 : 0 : ++p;
83 : 0 : last_synonyms.insert(string(p, len));
84 : 0 : p += len;
85 : : }
86 : 91 : }
87 : : }
88 : :
89 : 94 : last_synonyms.insert(synonym);
90 : 94 : }
91 : :
92 : : void
93 : 1 : FlintSynonymTable::remove_synonym(const string & term, const string & synonym)
94 : : {
95 [ + - ]: 1 : if (last_term != term) {
96 : 1 : merge_changes();
97 : 1 : last_term = term;
98 : :
99 : 1 : string tag;
100 [ + - ]: 1 : if (get_exact_entry(term, tag)) {
101 : 1 : const char * p = tag.data();
102 : 1 : const char * end = p + tag.size();
103 [ + + ]: 3 : while (p != end) {
104 : : size_t len;
105 [ + - ][ - + ]: 2 : if (p == end ||
[ - + ]
106 : : (len = byte(*p) ^ MAGIC_XOR_VALUE) >= size_t(end - p))
107 : 0 : throw Xapian::DatabaseCorruptError("Bad synonym data");
108 : 2 : ++p;
109 : 2 : last_synonyms.insert(string(p, len));
110 : 2 : p += len;
111 : : }
112 : 1 : }
113 : : }
114 : :
115 : 1 : last_synonyms.erase(synonym);
116 : 1 : }
117 : :
118 : : void
119 : 1 : FlintSynonymTable::clear_synonyms(const string & term)
120 : : {
121 : : // We don't actually ever need to merge_changes() here, but it's quite
122 : : // likely that someone might clear_synonyms() and then add_synonym() for
123 : : // the same term. The alternative we could otherwise optimise for (modify
124 : : // synonyms for a term, then clear those for another, then modify those for
125 : : // the first term again) seems much less likely.
126 [ - + ]: 1 : if (last_term == term) {
127 : 0 : last_synonyms.clear();
128 : : } else {
129 : 1 : merge_changes();
130 : 1 : last_term = term;
131 : : }
132 : 1 : }
133 : :
134 : : TermList *
135 : 47 : FlintSynonymTable::open_termlist(const string & term)
136 : : {
137 : 47 : vector<string> synonyms;
138 : :
139 [ + + ]: 47 : if (last_term == term) {
140 [ + + ]: 2 : if (last_synonyms.empty()) return NULL;
141 : :
142 : 1 : synonyms.reserve(last_synonyms.size());
143 : 1 : set<string>::const_iterator i;
144 [ + + ]: 3 : for (i = last_synonyms.begin(); i != last_synonyms.end(); ++i) {
145 : 2 : synonyms.push_back(*i);
146 : : }
147 : : } else {
148 : 45 : string tag;
149 [ + + ]: 45 : if (!get_exact_entry(term, tag)) return NULL;
150 : :
151 : 16 : const char * p = tag.data();
152 : 16 : const char * end = p + tag.size();
153 [ + + ]: 45 : while (p != end) {
154 : : size_t len;
155 [ + - ][ - + ]: 29 : if (p == end ||
[ - + ]
156 : : (len = byte(*p) ^ MAGIC_XOR_VALUE) >= size_t(end - p))
157 : 0 : throw Xapian::DatabaseCorruptError("Bad synonym data");
158 : 29 : ++p;
159 : 29 : synonyms.push_back(string(p, len));
160 : 29 : p += len;
161 [ + + ]: 45 : }
162 : : }
163 : :
164 : 47 : return new VectorTermList(synonyms.begin(), synonyms.end());
165 : : }
166 : :
167 : : ///////////////////////////////////////////////////////////////////////////
168 : :
169 : 11 : FlintSynonymTermList::~FlintSynonymTermList()
170 : : {
171 : : LOGCALL_DTOR(DB, "FlintSynonymTermList");
172 [ + - ][ # # ]: 11 : delete cursor;
[ # # ]
173 [ + - ][ # # ]: 11 : }
[ # # ]
174 : :
175 : : string
176 : 96 : FlintSynonymTermList::get_termname() const
177 : : {
178 : : LOGCALL(DB, string, "FlintSynonymTermList::get_termname", NO_ARGS);
179 : : Assert(cursor);
180 : : Assert(!cursor->current_key.empty());
181 : : Assert(!at_end());
182 : 96 : RETURN(cursor->current_key);
183 : : }
184 : :
185 : : Xapian::doccount
186 : 0 : FlintSynonymTermList::get_termfreq() const
187 : : {
188 : 0 : throw Xapian::InvalidOperationError("FlintSynonymTermList::get_termfreq() not meaningful");
189 : : }
190 : :
191 : : Xapian::termcount
192 : 0 : FlintSynonymTermList::get_collection_freq() const
193 : : {
194 : 0 : throw Xapian::InvalidOperationError("FlintSynonymTermList::get_collection_freq() not meaningful");
195 : : }
196 : :
197 : : TermList *
198 : 105 : FlintSynonymTermList::next()
199 : : {
200 : : LOGCALL(DB, TermList *, "FlintSynonymTermList::next", NO_ARGS);
201 : : Assert(!at_end());
202 : :
203 : 105 : cursor->next();
204 [ + + - + ]: 105 : if (!cursor->after_end() && !startswith(cursor->current_key, prefix)) {
[ - + ]
205 : : // We've reached the end of the end of the prefixed terms.
206 : 0 : cursor->to_end();
207 : : }
208 : :
209 : 105 : RETURN(NULL);
210 : : }
211 : :
212 : : TermList *
213 : 4 : FlintSynonymTermList::skip_to(const string &tname)
214 : : {
215 : : LOGCALL(DB, TermList *, "FlintSynonymTermList::skip_to", tname);
216 : : Assert(!at_end());
217 : :
218 [ + + ]: 4 : if (!cursor->find_entry_ge(tname)) {
219 : : // The exact term we asked for isn't there, so check if the next
220 : : // term after it also has the right prefix.
221 [ - + ][ # # ]: 2 : if (!cursor->after_end() && !startswith(cursor->current_key, prefix)) {
[ - + ]
222 : : // We've reached the end of the prefixed terms.
223 : 0 : cursor->to_end();
224 : : }
225 : : }
226 : 4 : RETURN(NULL);
227 : : }
228 : :
229 : : bool
230 : 109 : FlintSynonymTermList::at_end() const
231 : : {
232 : : LOGCALL(DB, bool, "FlintSynonymTermList::at_end", NO_ARGS);
233 : 109 : RETURN(cursor->after_end());
234 : : }
|