+size_t scan_docids(const vector<Needle> &needles, const vector<uint32_t> &docids, const Corpus &corpus, IOUringEngine *engine)
+{
+ Serializer docids_in_order;
+ unordered_map<string, bool> access_rx_cache;
+ uint64_t matched = 0;
+ for (size_t i = 0; i < docids.size(); ++i) {
+ uint32_t docid = docids[i];
+ corpus.get_compressed_filename_block(docid, [i, &matched, &needles, &access_rx_cache, &docids_in_order](string_view compressed) {
+ matched += scan_file_block(needles, compressed, &access_rx_cache, i, &docids_in_order);
+ });
+ }
+ engine->finish();
+ return matched;
+}
+
+// We do this sequentially, as it's faster than scattering
+// a lot of I/O through io_uring and hoping the kernel will
+// coalesce it plus readahead for us.
+uint64_t scan_all_docids(const vector<Needle> &needles, int fd, const Corpus &corpus, IOUringEngine *engine)
+{
+ unordered_map<string, bool> access_rx_cache;
+ uint32_t num_blocks = corpus.get_num_filename_blocks();
+ unique_ptr<uint64_t[]> offsets(new uint64_t[num_blocks + 1]);
+ complete_pread(fd, offsets.get(), (num_blocks + 1) * sizeof(uint64_t), corpus.offset_for_block(0));
+ string compressed;
+ uint64_t matched = 0;
+ for (uint32_t io_docid = 0; io_docid < num_blocks; io_docid += 32) {
+ uint32_t last_docid = std::min(io_docid + 32, num_blocks);
+ size_t io_len = offsets[last_docid] - offsets[io_docid];
+ if (compressed.size() < io_len) {
+ compressed.resize(io_len);
+ }
+ complete_pread(fd, &compressed[0], io_len, offsets[io_docid]);
+
+ for (uint32_t docid = io_docid; docid < last_docid; ++docid) {
+ size_t relative_offset = offsets[docid] - offsets[io_docid];
+ size_t len = offsets[docid + 1] - offsets[docid];
+ matched += scan_file_block(needles, { &compressed[relative_offset], len }, &access_rx_cache, 0, nullptr);
+ if (limit_matches <= 0)
+ return matched;
+ }
+ }
+ return matched;
+}
+
+// Takes the given posting list, unions it into the parts of the trigram disjunction
+// already read; if the list is complete, intersects with “cur_candidates”.
+//
+// Returns true if the search should be aborted (we are done).
+bool new_posting_list_read(TrigramDisjunction *td, vector<uint32_t> decoded, vector<uint32_t> *cur_candidates, vector<uint32_t> *tmp)
+{
+ if (td->docids.empty()) {
+ td->docids = move(decoded);
+ } else {
+ tmp->clear();
+ set_union(decoded.begin(), decoded.end(), td->docids.begin(), td->docids.end(), back_inserter(*tmp));
+ swap(*tmp, td->docids);
+ }
+ if (--td->remaining_trigrams_to_read > 0) {
+ // Need to wait for more.
+ if (ignore_case) {
+ dprintf(" ... %u reads left in OR group %u (%zu docids in list)\n",
+ td->remaining_trigrams_to_read, td->index, td->docids.size());
+ }
+ return false;
+ }
+ if (cur_candidates->empty()) {
+ if (ignore_case) {
+ dprintf(" ... all reads done for OR group %u (%zu docids)\n",
+ td->index, td->docids.size());
+ }
+ *cur_candidates = move(td->docids);
+ } else {
+ tmp->clear();
+ set_intersection(cur_candidates->begin(), cur_candidates->end(),
+ td->docids.begin(), td->docids.end(),
+ back_inserter(*tmp));
+ swap(*cur_candidates, *tmp);
+ if (ignore_case) {
+ if (cur_candidates->empty()) {
+ dprintf(" ... all reads done for OR group %u (%zu docids), intersected (none left, search is done)\n",
+ td->index, td->docids.size());
+ return true;
+ } else {
+ dprintf(" ... all reads done for OR group %u (%zu docids), intersected (%zu left)\n",
+ td->index, td->docids.size(), cur_candidates->size());
+ }
+ }
+ }
+ return false;
+}
+
+void do_search_file(const vector<Needle> &needles, const char *filename)