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