summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--build.zig47
-rw-r--r--build.zig.zon4
-rw-r--r--src/main.zig6
-rw-r--r--src/max_allocator.zig83
5 files changed, 141 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3cef7be
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+zig-cache/
diff --git a/build.zig b/build.zig
new file mode 100644
index 0000000..bba13ba
--- /dev/null
+++ b/build.zig
@@ -0,0 +1,47 @@
+const std = @import("std");
+
+// Although this function looks imperative, note that its job is to
+// declaratively construct a build graph that will be executed by an external
+// runner.
+pub fn build(b: *std.Build) void {
+ // Standard target options allows the person running `zig build` to choose
+ // what target to build for. Here we do not override the defaults, which
+ // means any target is allowed, and the default is native. Other options
+ // for restricting supported target set are available.
+ const target = b.standardTargetOptions(.{});
+
+ // Standard optimization options allow the person running `zig build` to select
+ // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
+ // set a preferred release mode, allowing the user to decide how to optimize.
+ const optimize = b.standardOptimizeOption(.{});
+
+ const lib = b.addStaticLibrary(.{
+ .name = "mf-zigtools",
+ // In this case the main source file is merely a path, however, in more
+ // complicated build scripts, this could be a generated file.
+ .root_source_file = .{ .path = "src/main.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ // This declares intent for the library to be installed into the standard
+ // location when the user invokes the "install" step (the default step when
+ // running `zig build`).
+ b.installArtifact(lib);
+
+ // Creates a step for unit testing. This only builds the test executable
+ // but does not run it.
+ const main_tests = b.addTest(.{
+ .root_source_file = .{ .path = "src/main.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const run_main_tests = b.addRunArtifact(main_tests);
+
+ // This creates a build step. It will be visible in the `zig build --help` menu,
+ // and can be selected like this: `zig build test`
+ // This will evaluate the `test` step rather than the default, which is "install".
+ const test_step = b.step("test", "Run library tests");
+ test_step.dependOn(&run_main_tests.step);
+}
diff --git a/build.zig.zon b/build.zig.zon
new file mode 100644
index 0000000..042c070
--- /dev/null
+++ b/build.zig.zon
@@ -0,0 +1,4 @@
+.{
+ .name = "mf-zigtools",
+ .version = "0.0.1",
+} \ No newline at end of file
diff --git a/src/main.zig b/src/main.zig
new file mode 100644
index 0000000..d5d8c78
--- /dev/null
+++ b/src/main.zig
@@ -0,0 +1,6 @@
+const ma = @import("max_allocator.zig");
+pub const MaxAllocator = ma.MaxAllocator;
+pub const maxAllocator = ma.maxAllocator;
+test {
+ _ = ma;
+}
diff --git a/src/max_allocator.zig b/src/max_allocator.zig
new file mode 100644
index 0000000..00b8630
--- /dev/null
+++ b/src/max_allocator.zig
@@ -0,0 +1,83 @@
+const std = @import("std");
+
+// Wrapper allocator that counts what the largest amount allocated at any time was from backing_allocator.
+pub const MaxAllocator = struct {
+ backing_allocator: std.mem.Allocator,
+ currently_allocated: usize = 0,
+ max_allocated: usize = 0,
+
+ pub fn allocator(self: *@This()) std.mem.Allocator {
+ return std.mem.Allocator{
+ .ptr = self,
+ .vtable = &.{
+ .alloc = rawAlloc,
+ .resize = rawResize,
+ .free = rawFree,
+ },
+ };
+ }
+
+ fn rawAlloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
+ var self: *@This() = @alignCast(@ptrCast(ctx));
+ var res = self.backing_allocator.rawAlloc(len, ptr_align, ret_addr);
+ if (res != null) {
+ self.currently_allocated += len;
+ std.log.warn("+{}", .{len});
+ self.max_allocated = @max(self.currently_allocated, self.max_allocated);
+ }
+ return res;
+ }
+
+ fn rawResize(ctx: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool {
+ var self: *@This() = @alignCast(@ptrCast(ctx));
+ const res = self.backing_allocator.rawResize(buf, buf_align, new_len, ret_addr);
+ if (res) {
+ self.currently_allocated -= buf.len;
+ std.log.warn("-{}", .{buf.len});
+ self.currently_allocated += new_len;
+ std.log.warn("+{}", .{new_len});
+ self.max_allocated = @max(self.currently_allocated, self.max_allocated);
+ }
+ return res;
+ }
+
+ fn rawFree(ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void {
+ var self: *@This() = @alignCast(@ptrCast(ctx));
+ self.backing_allocator.rawFree(buf, buf_align, ret_addr);
+ self.currently_allocated -= buf.len;
+ std.log.warn("-{}", .{buf.len});
+ }
+};
+
+pub fn maxAllocator(backing_allocator: std.mem.Allocator) MaxAllocator {
+ return MaxAllocator{ .backing_allocator = backing_allocator };
+}
+
+test {
+ const buflen = 2048;
+ var buf = [_]u8{0} ** buflen;
+ var fba = std.heap.FixedBufferAllocator.init(&buf);
+ var ma = maxAllocator(fba.allocator());
+ var a = ma.allocator();
+ var x = try a.alloc(u8, 100);
+ try std.testing.expectEqual(@as(usize, 100), ma.max_allocated);
+ try std.testing.expectEqual(@as(usize, 100), ma.currently_allocated);
+ var y = try a.alloc(u8, 10);
+ try std.testing.expectEqual(@as(usize, 110), ma.max_allocated);
+ try std.testing.expectEqual(@as(usize, 110), ma.currently_allocated);
+ a.free(x);
+ var z = try a.alloc(u8, 50);
+ try std.testing.expectEqual(@as(usize, 110), ma.max_allocated);
+ try std.testing.expectEqual(@as(usize, 60), ma.currently_allocated);
+
+ try std.testing.expect(a.resize(z, 1024));
+ try std.testing.expectEqual(@as(usize, 1034), ma.max_allocated);
+ try std.testing.expectEqual(@as(usize, 1034), ma.currently_allocated);
+
+ a.free(y);
+ a.free(z);
+
+ try std.testing.expectEqual(@as(usize, 1034), ma.max_allocated);
+ try std.testing.expectEqual(@as(usize, 0), ma.currently_allocated); // works on aarch64, not on x86_64 :thinking-face:
+ try std.testing.expectEqual(@as(usize, 0), fba.end_index);
+}