Reduce overheads of PostList positional data support
[xapian.git] / xapian-core / backends / glass / glass_positionlist.cc
blob201b0b51ced1bb18d8f0dde475511e5a633408cd
1 /* glass_positionlist.cc: A position list in a glass database.
3 * Copyright (C) 2004,2005,2006,2008,2009,2010,2013,2017 Olly Betts
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
18 * USA
21 #include <config.h>
23 #include "glass_positionlist.h"
25 #include <xapian/types.h>
27 #include "bitstream.h"
28 #include "debuglog.h"
29 #include "pack.h"
31 #include <string>
33 using namespace std;
35 void
36 GlassPositionListTable::pack(string & s,
37 const Xapian::VecCOW<Xapian::termpos> & vec) const
39 LOGCALL_VOID(DB, "GlassPositionListTable::pack", s | vec);
40 Assert(!vec.empty());
42 pack_uint(s, vec.back());
44 if (vec.size() > 1) {
45 BitWriter wr(s);
46 wr.encode(vec[0], vec.back());
47 wr.encode(vec.size() - 2, vec.back() - vec[0]);
48 wr.encode_interpolative(vec, 0, vec.size() - 1);
49 swap(s, wr.freeze());
53 Xapian::termcount
54 GlassPositionListTable::positionlist_count(Xapian::docid did,
55 const string & term) const
57 LOGCALL(DB, Xapian::termcount, "GlassPositionListTable::positionlist_count", did | term);
59 string data;
60 if (!get_exact_entry(make_key(did, term), data)) {
61 RETURN(0);
64 const char * pos = data.data();
65 const char * end = pos + data.size();
66 Xapian::termpos pos_last;
67 if (!unpack_uint(&pos, end, &pos_last)) {
68 throw Xapian::DatabaseCorruptError("Position list data corrupt");
70 if (pos == end) {
71 // Special case for single entry position list.
72 RETURN(1);
75 // Skip the header we just read.
76 BitReader rd(pos, end);
77 Xapian::termpos pos_first = rd.decode(pos_last);
78 Xapian::termpos pos_size = rd.decode(pos_last - pos_first) + 2;
79 RETURN(pos_size);
82 ///////////////////////////////////////////////////////////////////////////
84 Xapian::termcount
85 GlassBasePositionList::get_approx_size() const
87 LOGCALL(DB, Xapian::termcount, "GlassBasePositionList::get_approx_size", NO_ARGS);
88 RETURN(size);
91 Xapian::termpos
92 GlassBasePositionList::get_position() const
94 LOGCALL(DB, Xapian::termpos, "GlassBasePositionList::get_position", NO_ARGS);
95 Assert(have_started);
96 RETURN(current_pos);
99 bool
100 GlassBasePositionList::next()
102 LOGCALL(DB, bool, "GlassBasePositionList::next", NO_ARGS);
103 if (rare(!have_started)) {
104 have_started = true;
105 return current_pos <= last;
107 if (current_pos == last) {
108 return false;
110 current_pos = rd.decode_interpolative_next();
111 return true;
114 bool
115 GlassBasePositionList::skip_to(Xapian::termpos termpos)
117 LOGCALL(DB, bool, "GlassBasePositionList::skip_to", termpos);
118 have_started = true;
119 if (termpos >= last) {
120 if (termpos == last) {
121 current_pos = last;
122 return true;
124 return false;
126 while (current_pos < termpos) {
127 if (current_pos == last) {
128 return false;
130 current_pos = rd.decode_interpolative_next();
132 return true;
135 GlassPositionList::GlassPositionList(const string& data)
137 LOGCALL_CTOR(DB, "GlassPositionList", data);
139 have_started = false;
141 if (data.empty()) {
142 // There's no positional information for this term.
143 size = 0;
144 last = 0;
145 current_pos = 1;
146 return;
149 const char* pos = data.data();
150 const char* end = pos + data.size();
151 Xapian::termpos pos_last;
152 if (!unpack_uint(&pos, end, &pos_last)) {
153 throw Xapian::DatabaseCorruptError("Position list data corrupt");
156 if (pos == end) {
157 // Special case for single entry position list.
158 size = 1;
159 current_pos = last = pos_last;
160 return;
163 // Copy the rest of the data and lazily decode from that copy.
164 pos_data.assign(pos, end);
166 rd.init(pos_data.data(), pos_data.size());
167 Xapian::termpos pos_first = rd.decode(pos_last);
168 Xapian::termpos pos_size = rd.decode(pos_last - pos_first) + 2;
169 rd.decode_interpolative(0, pos_size - 1, pos_first, pos_last);
170 size = pos_size;
171 last = pos_last;
172 current_pos = pos_first;
175 GlassPositionList::GlassPositionList(const GlassTable* table,
176 Xapian::docid did,
177 const string& term)
179 LOGCALL_CTOR(DB, "GlassPositionList", table | did | term);
181 have_started = false;
183 if (!table->get_exact_entry(GlassPositionListTable::make_key(did, term),
184 pos_data)) {
185 // There's no positional information for this term.
186 size = 0;
187 last = 0;
188 current_pos = 1;
189 return;
192 const char* pos = pos_data.data();
193 const char* end = pos + pos_data.size();
194 Xapian::termpos pos_last;
195 if (!unpack_uint(&pos, end, &pos_last)) {
196 throw Xapian::DatabaseCorruptError("Position list data corrupt");
199 if (pos == end) {
200 // Special case for single entry position list.
201 size = 1;
202 current_pos = last = pos_last;
203 return;
206 rd.init(pos, end);
207 Xapian::termpos pos_first = rd.decode(pos_last);
208 Xapian::termpos pos_size = rd.decode(pos_last - pos_first) + 2;
209 rd.decode_interpolative(0, pos_size - 1, pos_first, pos_last);
210 size = pos_size;
211 last = pos_last;
212 current_pos = pos_first;
215 void
216 GlassRePositionList::read_data(Xapian::docid did,
217 const string& term)
219 LOGCALL_VOID(DB, "GlassRePositionList::read_data", table | did | term);
221 have_started = false;
223 if (!cursor.find_exact(GlassPositionListTable::make_key(did, term))) {
224 // There's no positional information for this term.
225 size = 0;
226 last = 0;
227 current_pos = 1;
228 return;
231 const char* pos = cursor.current_tag.data();
232 const char* end = pos + cursor.current_tag.size();
233 Xapian::termpos pos_last;
234 if (!unpack_uint(&pos, end, &pos_last)) {
235 throw Xapian::DatabaseCorruptError("Position list data corrupt");
238 if (pos == end) {
239 // Special case for single entry position list.
240 size = 1;
241 current_pos = last = pos_last;
242 return;
245 rd.init(pos, end);
246 Xapian::termpos pos_first = rd.decode(pos_last);
247 Xapian::termpos pos_size = rd.decode(pos_last - pos_first) + 2;
248 rd.decode_interpolative(0, pos_size - 1, pos_first, pos_last);
249 size = pos_size;
250 last = pos_last;
251 current_pos = pos_first;