1 #ifndef XAPIAN_INCLUDED_HONEY_CURSOR_H
2 #define XAPIAN_INCLUDED_HONEY_CURSOR_H
4 #include "honey_table.h"
6 #define DEBUGGING false
11 last_key
= std::string();
18 std::string current_key
, current_tag
;
19 mutable size_t val_size
= 0;
20 bool current_compressed
= false;
21 mutable CompressionStream comp_stream
;
22 bool is_at_end
= false;
23 mutable std::string last_key
;
25 // File offset to start of index and to current position in index.
28 // Forward to next constructor form.
29 explicit HoneyCursor(const HoneyTable
* table
)
30 : HoneyCursor(table
->fh
, table
->get_root()) {}
32 HoneyCursor(const BufferedFile
& fh_
, off_t root_
)
34 comp_stream(Z_DEFAULT_STRATEGY
),
41 HoneyCursor(const HoneyCursor
& o
)
43 current_key(o
.current_key
),
44 current_tag(o
.current_tag
), // FIXME really copy?
46 current_compressed(o
.current_compressed
),
47 comp_stream(Z_DEFAULT_STRATEGY
),
48 is_at_end(o
.is_at_end
),
53 fh
.set_pos(o
.fh
.get_pos());
56 bool after_end() const { return is_at_end
; }
65 // Skip val data we've not looked at.
70 if (fh
.get_pos() == root
) {
82 if (!last_key
.empty()) {
85 if (ch
== EOF
) throw Xapian::DatabaseError("EOF/error while reading key length", errno
);
89 if (!fh
.read(buf
, key_size
))
90 throw Xapian::DatabaseError("read of " + str(key_size
) + " bytes of key data failed", errno
);
91 current_key
.assign(last_key
, 0, reuse
);
92 current_key
.append(buf
, key_size
);
93 last_key
= current_key
;
97 description_append(esc
, current_key
);
98 std::cerr
<< "K:" << esc
<< std::endl
;
103 // FIXME: rework to take advantage of buffering that's happening anyway?
105 for (int i
= 0; i
< 8; ++i
) {
111 if (ch2
< 128) break;
116 const char* end
= p
+ r
;
117 if (!unpack_uint(&p
, end
, &val_size
)) {
118 throw Xapian::DatabaseError("val_size unpack_uint invalid");
120 if (p
!= end
) std::abort();
121 current_compressed
= val_size
& 1;
124 // FIXME: Always resize to 0? Not doing so avoids always having to
125 // clear all the data before reading it.
126 if (true && val_size
== 0)
127 current_tag
.resize(0);
133 bool read_tag(bool keep_compressed
= false) {
135 current_tag
.resize(val_size
);
136 if (!fh
.read(&(current_tag
[0]), val_size
))
137 throw Xapian::DatabaseError("read of " + str(val_size
) + " bytes of value data failed", errno
);
139 std::cerr
<< "read " << val_size
<< " bytes of value data ending @" << fh
.get_pos() << std::endl
;
144 description_append(esc
, current_tag
);
145 std::cerr
<< "V:" << esc
<< std::endl
;
148 if (!keep_compressed
&& current_compressed
) {
149 // Need to decompress.
150 comp_stream
.decompress_start();
152 if (!comp_stream
.decompress_chunk(current_tag
.data(), current_tag
.size(), new_tag
)) {
153 // Decompression didn't complete.
156 swap(current_tag
, new_tag
);
157 current_compressed
= false;
159 std::cerr
<< "decompressed to " << current_tag
.size() << " bytes of value data" << std::endl
;
162 return current_compressed
;
165 bool find_exact(const std::string
& key
) {
168 description_append(esc
, key
);
169 std::cerr
<< "find_exact(" << esc
<< ") @" << fh
.get_pos() << std::endl
;
175 int cmp0
= current_key
.compare(key
);
176 if (cmp0
== 0) return true;
183 int cmp
= current_key
.compare(key
);
184 if (cmp
== 0) return true;
190 bool find_entry_ge(const std::string
& key
) {
193 description_append(esc
, key
);
194 std::cerr
<< "find_entry_ge(" << esc
<< ") @" << fh
.get_pos() << std::endl
;
200 int cmp0
= current_key
.compare(key
);
201 if (cmp0
== 0) return true;
208 int cmp
= current_key
.compare(key
);
209 if (cmp
== 0) return true;
210 if (cmp
> 0) return false;
216 bool find_entry(const std::string
& key
) {
219 description_append(desc
, key
);
220 std::cerr
<< "find_entry(" << desc
<< ") [LE]" << std::endl
;
227 int cmp0
= current_key
.compare(key
);
228 if (cmp0
== 0) return true;
238 while (pos
= fh
.get_pos(), k
= current_key
, vs
= val_size
, compressed
= current_compressed
, next()) {
239 int cmp
= current_key
.compare(key
);
240 if (cmp
== 0) return true;
244 current_key
= last_key
= k
;
246 current_compressed
= compressed
;
252 int cmp0
= current_key
.compare(key
);
254 if (DEBUGGING
) std::cerr
<< " already on it" << std::endl
;
262 // FIXME: If "close" just seek forwards? Or consider seeking from current index pos?
263 //off_t pos = fh.get_pos();
265 std::string index_key
, prev_index_key
;
266 std::make_unsigned
<off_t
>::type ptr
= 0;
268 int reuse
= fh
.read();
269 if (reuse
== -1) break;
271 if (len
== -1) std::abort(); // FIXME
272 prev_index_key
= index_key
;
273 index_key
.resize(reuse
+ len
);
274 fh
.read(&index_key
[reuse
], len
);
278 description_append(desc
, index_key
);
279 std::cerr
<< "Index key: " << desc
<< std::endl
;
282 cmp0
= index_key
.compare(key
);
284 last_key
= ptr
? index_key
: std::string(); // for now (a lie, but the reuse part is correct).
290 if ((b
& 0x80) == 0) break;
293 if (!unpack_uint(&p
, e
, &ptr
) || p
!= e
) std::abort(); // FIXME
294 if (DEBUGGING
) std::cerr
<< " -> " << ptr
<< std::endl
;
296 if (DEBUGGING
) std::cerr
<< " hit straight from index" << std::endl
;
298 current_key
= index_key
;
301 // FIXME: rework to take advantage of buffering that's happening anyway?
303 for (int i
= 0; i
< 8; ++i
) {
309 if (ch2
< 128) break;
314 const char* end
= p
+ r
;
315 if (!unpack_uint(&p
, end
, &val_size
)) {
316 throw Xapian::DatabaseError("val_size unpack_uint invalid");
318 bool& compressed
= current_compressed
;
319 compressed
= val_size
& 1;
321 if (p
!= end
) std::abort();
325 if (DEBUGGING
) std::cerr
<< " cmp0 = " << cmp0
<< ", going to " << ptr
<< std::endl
;
328 // FIXME: crude for now
330 current_key
= prev_index_key
;
334 // FIXME: rework to take advantage of buffering that's happening anyway?
336 for (int i
= 0; i
< 8; ++i
) {
342 if (ch2
< 128) break;
347 const char* end
= p
+ r
;
348 if (!unpack_uint(&p
, end
, &val_size
)) {
349 throw Xapian::DatabaseError("val_size unpack_uint invalid");
351 bool& compressed
= current_compressed
;
352 auto& val
= current_tag
;
353 compressed
= val_size
& 1;
356 if (p
!= end
) std::abort();
357 val_size
-= (end
- p
);
359 size_t n
= std::min(val_size
, sizeof(buf
));
360 if (!fh
.read(buf
, n
))
361 throw Xapian::DatabaseError("read of " + str(n
) + "/" + str(val_size
) + " bytes of value data failed", errno
);
373 description_append(desc
, current_key
);
374 std::cerr
<< "Dropped to data layer on key: " << desc
<< std::endl
;
377 // FIXME: need to put us in the "read key not tag" state but persist that more?
378 // if (cmp0 == 0) this is an exact hit from the index...
390 compressed
= current_compressed
;
394 description_append(desc
, current_key
);
395 std::cerr
<< "@ " << pos
<< " key: " << desc
<< std::endl
;
400 int cmp
= current_key
.compare(key
);
402 if (DEBUGGING
) std::cerr
<< " found it" << std::endl
;
410 description_append(desc
, current_key
);
411 std::cerr
<< " NOT found - reached " << desc
<< std::endl
;
413 // No match so back up to previous entry.
415 last_key
= current_key
= k
;
417 current_compressed
= compressed
;
421 description_append(desc
, current_key
);
422 std::cerr
<< " NOT found - leaving us on " << desc
<< std::endl
;
428 void find_entry_lt(const std::string
& key
) {
431 description_append(esc
, key
);
432 std::cerr
<< "find_entry_lt(" << esc
<< ") @" << fh
.get_pos() << std::endl
;
435 if (is_at_end
|| key
< current_key
) {
437 current_key
.resize(0);
448 compressed
= current_compressed
;
449 } while (next() && current_key
< key
);
451 // Back up to previous entry.
453 last_key
= current_key
= k
;
455 current_compressed
= compressed
;
459 HoneyCursor
* clone() const {
460 return new HoneyCursor(*this);
463 bool del() { return false; }
466 class MutableHoneyCursor
: public HoneyCursor
{
468 MutableHoneyCursor(HoneyTable
* table_
)
469 : HoneyCursor(table_
->fh
, table_
->get_root()) { }
472 #endif // XAPIAN_INCLUDED_HONEY_CURSOR_H