const std = @import("std"); const testing = std.testing; pub const GitAllocator = struct { mutex: std.Thread.Mutex, alloc: std.mem.Allocator, allocs: std.AutoArrayHashMap(*anyopaque, []u8) = undefined, pub fn init(alloc: std.mem.Allocator) GitAllocator { return GitAllocator{ .alloc = alloc, .allocs = std.AutoArrayHashMap(*anyopaque, []u8).init(alloc), .mutex = std.Thread.Mutex{}, }; } fn nalloc(self: *GitAllocator, size: usize) ?*anyopaque { const frame = self.alloc.alloc(u8, size) catch return null; self.allocs.put(frame.ptr, frame) catch return null; return frame.ptr; } pub fn malloc(self: *GitAllocator, size: usize) ?*anyopaque { self.mutex.lock(); defer self.mutex.unlock(); return self.nalloc(size); } pub fn realloc(self: *GitAllocator, nptr: ?*anyopaque, size: usize) ?*anyopaque { self.mutex.lock(); defer self.mutex.unlock(); const ptr = nptr orelse return self.nalloc(size); const frame = self.allocs.get(ptr) orelse return null; if (!self.allocs.swapRemove(ptr)) { @panic("failed to remove"); } const new_frame = self.alloc.realloc(frame, size) catch return null; self.allocs.put(new_frame.ptr, new_frame) catch return null; return new_frame.ptr; } pub fn free(self: *GitAllocator, nptr: ?*anyopaque) void { self.mutex.lock(); defer self.mutex.unlock(); const ptr = nptr orelse return; const frame = self.allocs.get(ptr) orelse return; defer self.alloc.free(frame); if (!self.allocs.swapRemove(ptr)) { @panic("failed to remove"); } } pub fn deinit(self: *GitAllocator) void { self.allocs.deinit(); } }; test "test git allocator" { var gitAlloc = GitAllocator.init(testing.allocator); defer gitAlloc.deinit(); var ptr = gitAlloc.malloc(2_000); try testing.expect(ptr != null); ptr = gitAlloc.realloc(ptr, 4_000); ptr = gitAlloc.realloc(ptr, 1_000); gitAlloc.free(ptr); }