Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/index.zig
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,16 @@ pub const WordIndex = struct {
return @intCast(self.file_words.count());
}

/// Shrink all hit lists and per-file word sets to release excess capacity.
pub fn shrinkAllocations(self: *WordIndex) void {
var iter = self.index.iterator();
while (iter.next()) |entry| {
if (entry.value_ptr.capacity > entry.value_ptr.items.len) {
entry.value_ptr.shrinkAndFree(self.allocator, entry.value_ptr.items.len);
}
}
}

pub const DiskHeader = struct {
file_count: u32,
git_head: ?[40]u8,
Expand Down Expand Up @@ -528,6 +538,10 @@ pub const TrigramIndex = struct {
/// When true, deinit frees the path keys in file_trigrams (set by readFromDisk).
owns_paths: bool = false,

/// Maximum entries per posting list — caps memory for common trigrams.
/// Trigrams appearing in more files than this are poor discriminators anyway.
const MAX_POSTINGS: usize = 512;

pub fn init(allocator: std.mem.Allocator) TrigramIndex {
return .{
.index = std.AutoHashMap(Trigram, PostingList).init(allocator),
Expand Down Expand Up @@ -1174,6 +1188,24 @@ pub const TrigramIndex = struct {
return @intCast(self.file_trigrams.count());
}

/// Shrink all posting lists to their actual length, releasing excess capacity.
/// Call after bulk indexing to reclaim ArrayList over-allocation (~50% savings).
pub fn shrinkPostingLists(self: *TrigramIndex) void {
var iter = self.index.iterator();
while (iter.next()) |entry| {
if (entry.value_ptr.items.capacity > entry.value_ptr.items.items.len) {
entry.value_ptr.items.shrinkAndFree(self.allocator, entry.value_ptr.items.items.len);
}
}
// Also shrink file_trigrams lists
var ft_iter = self.file_trigrams.iterator();
while (ft_iter.next()) |entry| {
if (entry.value_ptr.capacity > entry.value_ptr.items.len) {
entry.value_ptr.shrinkAndFree(self.allocator, entry.value_ptr.items.len);
}
}
}

/// Header info that can be read without loading the full index.
pub const DiskHeader = struct {
file_count: u32,
Expand Down
3 changes: 3 additions & 0 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,9 @@ fn scanBg(store: *Store, explorer: *Explorer, root: []const u8, allocator: std.m
explorer.releaseContents();
explorer.releaseSecondaryIndexes();
}
// Shrink index allocations to reclaim ArrayList over-allocation
if (explorer.trigram_index.asHeap()) |heap| heap.shrinkPostingLists();
explorer.word_index.shrinkAllocations();
return;
}
if (TrigramIndex.readFromDisk(data_dir, allocator)) |loaded| {
Expand Down
Loading