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