1const std = @import("std");
2const testing = std.testing;
3
4pub const GitAllocator = struct {
5 mutex: std.Thread.Mutex,
6 alloc: std.mem.Allocator,
7 allocs: std.AutoArrayHashMap(*anyopaque, []u8) = undefined,
8
9 pub fn init(alloc: std.mem.Allocator) GitAllocator {
10 return GitAllocator{
11 .alloc = alloc,
12 .allocs = std.AutoArrayHashMap(*anyopaque, []u8).init(alloc),
13 .mutex = std.Thread.Mutex{},
14 };
15 }
16
17 fn nalloc(self: *GitAllocator, size: usize) ?*anyopaque {
18 const frame = self.alloc.alloc(u8, size) catch return null;
19 self.allocs.put(frame.ptr, frame) catch return null;
20 return frame.ptr;
21 }
22
23 pub fn malloc(self: *GitAllocator, size: usize) ?*anyopaque {
24 self.mutex.lock();
25 defer self.mutex.unlock();
26 return self.nalloc(size);
27 }
28
29 pub fn realloc(self: *GitAllocator, nptr: ?*anyopaque, size: usize) ?*anyopaque {
30 self.mutex.lock();
31 defer self.mutex.unlock();
32
33 const ptr = nptr orelse return self.nalloc(size);
34
35 const frame = self.allocs.get(ptr) orelse return null;
36 if (!self.allocs.swapRemove(ptr)) {
37 @panic("failed to remove");
38 }
39
40 const new_frame = self.alloc.realloc(frame, size) catch return null;
41
42 self.allocs.put(new_frame.ptr, new_frame) catch return null;
43 return new_frame.ptr;
44 }
45
46 pub fn free(self: *GitAllocator, nptr: ?*anyopaque) void {
47 self.mutex.lock();
48 defer self.mutex.unlock();
49
50 const ptr = nptr orelse return;
51
52 const frame = self.allocs.get(ptr) orelse return;
53
54 defer self.alloc.free(frame);
55 if (!self.allocs.swapRemove(ptr)) {
56 @panic("failed to remove");
57 }
58 }
59
60 pub fn deinit(self: *GitAllocator) void {
61 self.allocs.deinit();
62 }
63};
64
65test "test git allocator" {
66 var gitAlloc = GitAllocator.init(testing.allocator);
67 defer gitAlloc.deinit();
68
69 var ptr = gitAlloc.malloc(2_000);
70
71 try testing.expect(ptr != null);
72
73 ptr = gitAlloc.realloc(ptr, 4_000);
74 ptr = gitAlloc.realloc(ptr, 1_000);
75
76 gitAlloc.free(ptr);
77}