╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ search.zig ───────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯
164 } 165 166 // start the next result at `key` rather than the first one. 167 pub fn seek(self: *Self, key: []const u8) void { 168 @memcpy(self.pos[0..key.len], key); 169 self.pos_len = key.len; 170 self.returned = false; 171 } 172 173 pub fn next(self: *Self) !?[]const u8 { 174 // with no terms at all nothing narrows the results, so none are listed 175 if (self.terms.items.len == 0) return null; 176 if (self.returned) { 177 // doc keys are at most max_doc_key_len, so appending a byte 178 // lands strictly after the one just returned 179 self.pos[self.pos_len] = 0; 180 self.pos_len += 1; 181 self.returned = false; 182 } 183 // intersect by seeking: a term that lands past the candidate makes 184 // its key the new candidate and the others seek up to it. 185 var i: usize = 0; 186 while (i < self.terms.items.len) { 187 const key = (try self.terms.items[i].seek(self.pos[0..self.pos_len])) orelse return null; 188 if (std.mem.order(u8, key, self.pos[0..self.pos_len]) == .gt) { 189 @memcpy(self.pos[0..key.len], key); 190 self.pos_len = key.len; 191 i = 0; 192 continue; 193 } 194 i += 1; 195 } 196 self.returned = true; 197 return self.pos[0..self.pos_len]; 198 } 199 }; 200 } 201 202 // the unique lowercased words of `text`, in the order they first appear. 203 fn tokenize(aa: std.mem.Allocator, text: []const u8) ![]const []const u8 { 204 var words: std.StringArrayHashMapUnmanaged(void) = .empty; 205 const limit = @min(text.len, max_indexed_bytes); 206 var i: usize = 0; 207 while (i < limit) { 208 if (!isWordByte(text[i])) { 209 i += 1; 210 continue; 211 } 212 const start = i; 213 while (i < limit and isWordByte(text[i])) i += 1; 214 const word = text[start..@min(i, start + max_word_len)]; 215 if (word.len < min_word_len) continue; 216 const lowered = try aa.alloc(u8, word.len); 217 _ = std.ascii.lowerString(lowered, word); 218 try words.put(aa, lowered, {}); 219 if (words.count() == max_words_per_doc) break; 220 } 221 return words.keys(); 222 } 223 224 // a word is a run of ascii alphanumerics and non-ascii bytes. 225 fn isWordByte(byte: u8) bool { 226 return std.ascii.isAlphanumeric(byte) or byte >= 0x80; 227 } 228