4 #include <unordered_map>
11 #include <arpa/inet.h>
16 #define P4NENC_BOUND(n) ((n+127)/128+(n+32)*sizeof(uint32_t))
19 using namespace std::chrono;
22 //#define dprintf(...) fprintf(stderr, __VA_ARGS__);
24 static inline uint32_t read_unigram(const string &s, size_t idx)
27 return (unsigned char)s[idx];
33 static inline uint32_t read_trigram(const string &s, size_t start)
35 return read_unigram(s, start) |
36 (read_unigram(s, start + 1) << 8) |
37 (read_unigram(s, start + 2) << 16);
40 bool has_access(const char *filename, unordered_map<string, bool> *access_rx_cache)
42 const char *end = strchr(filename + 1, '/');
43 while (end != nullptr) {
44 string parent_path(filename, end);
45 auto it = access_rx_cache->find(parent_path);
47 if (it == access_rx_cache->end()) {
48 ok = access(parent_path.c_str(), R_OK | X_OK) == 0;
49 access_rx_cache->emplace(move(parent_path), ok);
56 end = strchr(end + 1, '/');
60 // Check for rx first in the cache; if that isn't true, check R_OK uncached.
61 // This is roughly the same thing as mlocate does.
62 auto it = access_rx_cache->find(filename);
63 if (it != access_rx_cache->end() && it->second) {
67 return access(filename, R_OK) == 0;
78 void do_search_file(const string &needle, const char *filename)
80 int fd = open(filename, O_RDONLY);
87 if (setgid(getgid()) != 0) {
92 //steady_clock::time_point start = steady_clock::now();
93 if (access("/", R_OK | X_OK)) {
94 // We can't find anything, no need to bother...
98 off_t len = lseek(fd, 0, SEEK_END);
103 const char *data = (char *)mmap(nullptr, len, PROT_READ, MAP_SHARED, fd, /*offset=*/0);
104 if (data == MAP_FAILED) {
108 uint64_t num_trigrams = *(const uint64_t *)data;
109 uint64_t filename_index_offset = *(const uint64_t *)(data + sizeof(uint64_t));
111 const Trigram *trgm_begin = (Trigram *)(data + sizeof(uint64_t) * 2);
112 const Trigram *trgm_end = trgm_begin + num_trigrams;
114 vector<const Trigram *> trigrams;
115 for (size_t i = 0; i < needle.size() - 2; ++i) {
116 uint32_t trgm = read_trigram(needle, i);
117 const Trigram *trgmptr = lower_bound(trgm_begin, trgm_end, trgm, [](const Trigram &trgm, uint32_t t) {
118 return trgm.trgm < t;
120 if (trgmptr == trgm_end || trgmptr->trgm != trgm) {
121 dprintf("trigram %06x isn't found, we abort the search\n", trgm);
122 munmap((void *)data, len);
126 trigrams.push_back(trgmptr);
128 sort(trigrams.begin(), trigrams.end());
130 auto last = unique(trigrams.begin(), trigrams.end());
131 trigrams.erase(last, trigrams.end());
133 sort(trigrams.begin(), trigrams.end(), [&](const Trigram *a, const Trigram *b) {
134 return a->num_docids < b->num_docids;
137 vector<uint32_t> in1, in2, out;
138 for (const Trigram *trgmptr : trigrams) {
139 //uint32_t trgm = trgmptr->trgm;
140 size_t num = trgmptr->num_docids;
141 unsigned char *pldata = (unsigned char *)(data + trgmptr->offset);
143 in1.resize(num + 128);
144 p4nd1dec128v32((unsigned char *)pldata, num, &in1[0]);
146 dprintf("trigram '%c%c%c' decoded to %zu entries\n", trgm & 0xff, (trgm >> 8) & 0xff, (trgm >> 16) & 0xff, num);
148 if (num > in1.size() * 100) {
149 dprintf("trigram '%c%c%c' has %zu entries, ignoring the rest (will weed out false positives later)\n",
150 trgm & 0xff, (trgm >> 8) & 0xff, (trgm >> 16) & 0xff, num);
154 if (in2.size() < num + 128) {
155 in2.resize(num + 128);
157 p4nd1dec128v32((unsigned char *)pldata, num, &in2[0]);
160 set_intersection(in1.begin(), in1.end(), in2.begin(), in2.begin() + num, back_inserter(out));
162 dprintf("trigram '%c%c%c' decoded to %zu entries, %zu left\n", trgm & 0xff, (trgm >> 8) & 0xff, (trgm >> 16) & 0xff, num, in1.size());
164 dprintf("no matches (intersection list is empty)\n");
169 steady_clock::time_point end = steady_clock::now();
171 dprintf("Intersection took %.1f ms. Doing final verification and printing:\n",
172 1e3 * duration<float>(end - start).count());
174 unordered_map<string, bool> access_rx_cache;
176 const uint64_t *filename_offsets = (const uint64_t *)(data + filename_index_offset);
178 for (uint32_t docid : in1) {
179 const char *filename = (const char *)(data + filename_offsets[docid]);
180 if (strstr(filename, needle.c_str()) == nullptr) {
183 if (has_access(filename, &access_rx_cache)) {
185 printf("%s\n", filename);
188 end = steady_clock::now();
189 dprintf("Done in %.1f ms, found %d matches.\n",
190 1e3 * duration<float>(end - start).count(), matched);
192 munmap((void *)data, len);
196 int main(int argc, char **argv)
198 //do_search_file(argv[1], "all.trgm");
199 do_search_file(argv[1], "/var/lib/mlocate/plocate.db");