├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── build.zig ├── licenses.txt ├── src ├── CountingAllocator.zig ├── FailingAllocator.zig ├── LimitingAllocator.zig ├── LivenessAllocator.zig ├── PanicAllocator.zig ├── UnreachableAllocator.zig ├── ZeroAllocator.zig └── lib.zig ├── test.zig └── zigmod.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.zig text eol=lf 3 | zigmod.* text eol=lf 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .zig-cache 2 | zig-out 3 | .zigmod 4 | deps.zig 5 | files.zig 6 | zigmod.lock 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Meghan Denny 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gimme 2 | 3 | ![loc](https://sloc.xyz/github/nektro/gimme) 4 | [![license](https://img.shields.io/github/license/nektro/gimme.svg)](https://github.com/nektro/gimme/blob/master/LICENSE) 5 | [![nektro @ github sponsors](https://img.shields.io/badge/sponsors-nektro-purple?logo=github)](https://github.com/sponsors/nektro) 6 | [![Zig](https://img.shields.io/badge/Zig-0.14-f7a41d)](https://ziglang.org/) 7 | [![Zigmod](https://img.shields.io/badge/Zigmod-latest-f7a41d)](https://github.com/nektro/zigmod) 8 | 9 | `gimme` is a yummy collection of useful `Allocator`s. 10 | 11 | Check out [`src/lib.zig`](src/lib.zig) for more. 12 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const deps = @import("./deps.zig"); 3 | 4 | pub fn build(b: *std.Build) void { 5 | const target = b.standardTargetOptions(.{}); 6 | const mode = b.option(std.builtin.Mode, "mode", "") orelse .Debug; 7 | const disable_llvm = b.option(bool, "disable_llvm", "use the non-llvm zig codegen") orelse false; 8 | 9 | const exe_tests = b.addTest(.{ 10 | .root_source_file = b.path("test.zig"), 11 | .target = target, 12 | .optimize = mode, 13 | }); 14 | deps.addAllTo(exe_tests); 15 | exe_tests.use_llvm = !disable_llvm; 16 | exe_tests.use_lld = !disable_llvm; 17 | 18 | const tests_run = b.addRunArtifact(exe_tests); 19 | tests_run.has_side_effects = true; 20 | 21 | const test_step = b.step("test", "Run all library tests"); 22 | test_step.dependOn(&tests_run.step); 23 | } 24 | -------------------------------------------------------------------------------- /licenses.txt: -------------------------------------------------------------------------------- 1 | MIT: 2 | = https://spdx.org/licenses/MIT 3 | - This 4 | -------------------------------------------------------------------------------- /src/CountingAllocator.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const CountingAllocator = @This(); 3 | 4 | child_allocator: std.mem.Allocator, 5 | count_active: u64, 6 | count_total: u64, 7 | count_allocs: u64, 8 | count_allocs_success: u64, 9 | count_resizes: u64, 10 | count_frees: u64, 11 | 12 | pub fn init(child_allocator: std.mem.Allocator) CountingAllocator { 13 | return .{ 14 | .child_allocator = child_allocator, 15 | .count_active = 0, 16 | .count_total = 0, 17 | .count_allocs = 0, 18 | .count_allocs_success = 0, 19 | .count_resizes = 0, 20 | .count_frees = 0, 21 | }; 22 | } 23 | 24 | pub fn allocator(self: *CountingAllocator) std.mem.Allocator { 25 | return .{ 26 | .ptr = self, 27 | .vtable = &.{ 28 | .alloc = alloc, 29 | .resize = resize, 30 | .remap = remap, 31 | .free = free, 32 | }, 33 | }; 34 | } 35 | 36 | fn alloc(ctx: *anyopaque, len: usize, ptr_align: std.mem.Alignment, ret_addr: usize) ?[*]u8 { 37 | var self: *CountingAllocator = @ptrCast(@alignCast(ctx)); 38 | self.count_allocs += 1; 39 | const ptr = self.child_allocator.rawAlloc(len, ptr_align, ret_addr) orelse return null; 40 | self.count_allocs_success += 1; 41 | self.count_active += len; 42 | self.count_total += len; 43 | return ptr; 44 | } 45 | 46 | fn resize(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, new_len: usize, ret_addr: usize) bool { 47 | var self: *CountingAllocator = @ptrCast(@alignCast(ctx)); 48 | self.count_resizes += 1; 49 | const old_len = buf.len; 50 | const stable = self.child_allocator.rawResize(buf, buf_align, new_len, ret_addr); 51 | if (stable) { 52 | if (new_len > old_len) { 53 | self.count_active += new_len; 54 | self.count_active -= old_len; 55 | self.count_total += new_len; 56 | self.count_total -= old_len; 57 | } else { 58 | self.count_active -= old_len; 59 | self.count_active += new_len; 60 | self.count_total -= old_len; 61 | self.count_total += new_len; 62 | } 63 | } 64 | return stable; 65 | } 66 | 67 | fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { 68 | var self: *CountingAllocator = @ptrCast(@alignCast(ctx)); 69 | const memory_new = self.child_allocator.rawRemap(memory, alignment, new_len, ret_addr) orelse return null; 70 | if (memory_new != memory.ptr) self.count_active -= memory.len; 71 | if (memory_new != memory.ptr) self.count_active += new_len; 72 | if (memory_new != memory.ptr) self.count_total -= memory.len; 73 | if (memory_new != memory.ptr) self.count_total += new_len; 74 | return memory_new; 75 | } 76 | 77 | fn free(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, ret_addr: usize) void { 78 | var self: *CountingAllocator = @ptrCast(@alignCast(ctx)); 79 | self.count_frees += 1; 80 | self.count_active -= buf.len; 81 | return self.child_allocator.rawFree(buf, buf_align, ret_addr); 82 | } 83 | -------------------------------------------------------------------------------- /src/FailingAllocator.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const FailingAllocator = @This(); 3 | 4 | pub fn allocator() std.mem.Allocator { 5 | return .{ 6 | .ptr = undefined, 7 | .vtable = &.{ 8 | .alloc = alloc, 9 | .resize = resize, 10 | .remap = remap, 11 | .free = free, 12 | }, 13 | }; 14 | } 15 | 16 | fn alloc(ctx: *anyopaque, len: usize, ptr_align: std.mem.Alignment, ret_addr: usize) ?[*]u8 { 17 | _ = ctx; 18 | _ = len; 19 | _ = ptr_align; 20 | _ = ret_addr; 21 | return null; 22 | } 23 | 24 | fn resize(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, new_len: usize, ret_addr: usize) bool { 25 | _ = ctx; 26 | _ = buf; 27 | _ = buf_align; 28 | _ = new_len; 29 | _ = ret_addr; 30 | return false; 31 | } 32 | 33 | fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { 34 | _ = ctx; 35 | _ = memory; 36 | _ = alignment; 37 | _ = new_len; 38 | _ = ret_addr; 39 | return null; 40 | } 41 | 42 | fn free(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, ret_addr: usize) void { 43 | _ = ctx; 44 | _ = buf; 45 | _ = buf_align; 46 | _ = ret_addr; 47 | return {}; 48 | } 49 | 50 | test FailingAllocator { 51 | try std.testing.expectError(error.OutOfMemory, FailingAllocator.allocator().alloc(u8, 1)); 52 | } 53 | -------------------------------------------------------------------------------- /src/LimitingAllocator.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const LimitingAllocator = @This(); 3 | const gimme = @import("./lib.zig"); 4 | 5 | counter: gimme.CountingAllocator, 6 | limit: u64, 7 | 8 | pub fn init(child_allocator: std.mem.Allocator, limit: u64) LimitingAllocator { 9 | return .{ 10 | .counter = gimme.CountingAllocator.init(child_allocator), 11 | .limit = limit, 12 | }; 13 | } 14 | 15 | pub fn allocator(self: *LimitingAllocator) std.mem.Allocator { 16 | return .{ 17 | .ptr = self, 18 | .vtable = &.{ 19 | .alloc = alloc, 20 | .resize = resize, 21 | .remap = remap, 22 | .free = free, 23 | }, 24 | }; 25 | } 26 | 27 | fn alloc(ctx: *anyopaque, len: usize, ptr_align: std.mem.Alignment, ret_addr: usize) ?[*]u8 { 28 | var self: *LimitingAllocator = @ptrCast(@alignCast(ctx)); 29 | if (self.counter.count_active + len > self.limit) return null; 30 | return self.counter.allocator().rawAlloc(len, ptr_align, ret_addr) orelse return null; 31 | } 32 | 33 | fn resize(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, new_len: usize, ret_addr: usize) bool { 34 | var self: *LimitingAllocator = @ptrCast(@alignCast(ctx)); 35 | const old_len = buf.len; 36 | if (self.counter.count_active - old_len + new_len > self.limit) return false; 37 | return self.counter.allocator().rawResize(buf, buf_align, new_len, ret_addr); 38 | } 39 | 40 | fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { 41 | var self: *LimitingAllocator = @ptrCast(@alignCast(ctx)); 42 | if (self.counter.count_active - memory.len + new_len > self.limit) return null; 43 | return self.counter.allocator().rawRemap(memory, alignment, new_len, ret_addr); 44 | } 45 | 46 | fn free(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, ret_addr: usize) void { 47 | var self: *LimitingAllocator = @ptrCast(@alignCast(ctx)); 48 | return self.counter.allocator().rawFree(buf, buf_align, ret_addr); 49 | } 50 | -------------------------------------------------------------------------------- /src/LivenessAllocator.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const LivenessAllocator = @This(); 3 | 4 | child_allocator: std.mem.Allocator, 5 | count: u64, 6 | 7 | pub fn init(child_allocator: std.mem.Allocator) LivenessAllocator { 8 | return .{ 9 | .child_allocator = child_allocator, 10 | .count = 0, 11 | }; 12 | } 13 | 14 | pub fn allocator(self: *LivenessAllocator) std.mem.Allocator { 15 | return .{ 16 | .ptr = self, 17 | .vtable = &.{ 18 | .alloc = alloc, 19 | .resize = resize, 20 | .remap = remap, 21 | .free = free, 22 | }, 23 | }; 24 | } 25 | 26 | fn alloc(ctx: *anyopaque, len: usize, ptr_align: std.mem.Alignment, ret_addr: usize) ?[*]u8 { 27 | var self: *LivenessAllocator = @ptrCast(@alignCast(ctx)); 28 | const ptr = self.child_allocator.rawAlloc(len, ptr_align, ret_addr) orelse return null; 29 | self.count += len; 30 | return ptr; 31 | } 32 | 33 | fn resize(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, new_len: usize, ret_addr: usize) bool { 34 | var self: *LivenessAllocator = @ptrCast(@alignCast(ctx)); 35 | const stable = self.child_allocator.rawResize(buf, buf_align, new_len, ret_addr); 36 | if (!stable) self.count += new_len; 37 | return stable; 38 | } 39 | 40 | fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { 41 | var self: *LivenessAllocator = @ptrCast(@alignCast(ctx)); 42 | const memory_new = self.child_allocator.rawRemap(memory, alignment, new_len, ret_addr) orelse return null; 43 | if (memory_new != memory.ptr) self.count -= memory.len; 44 | if (memory_new != memory.ptr) self.count += new_len; 45 | return memory_new; 46 | } 47 | 48 | fn free(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, ret_addr: usize) void { 49 | var self: *LivenessAllocator = @ptrCast(@alignCast(ctx)); 50 | defer self.count -= buf.len; 51 | return self.child_allocator.rawFree(buf, buf_align, ret_addr); 52 | } 53 | -------------------------------------------------------------------------------- /src/PanicAllocator.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const PanicAllocator = @This(); 3 | 4 | pub fn allocator() std.mem.Allocator { 5 | return .{ 6 | .ptr = undefined, 7 | .vtable = &.{ 8 | .alloc = alloc, 9 | .resize = resize, 10 | .remap = remap, 11 | .free = free, 12 | }, 13 | }; 14 | } 15 | 16 | fn alloc(ctx: *anyopaque, len: usize, ptr_align: std.mem.Alignment, ret_addr: usize) ?[*]u8 { 17 | _ = ctx; 18 | _ = len; 19 | _ = ptr_align; 20 | _ = ret_addr; 21 | @panic("out of memory"); 22 | } 23 | 24 | fn resize(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, new_len: usize, ret_addr: usize) bool { 25 | _ = ctx; 26 | _ = buf; 27 | _ = buf_align; 28 | _ = new_len; 29 | _ = ret_addr; 30 | @panic("out of memory"); 31 | } 32 | 33 | fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { 34 | _ = ctx; 35 | _ = memory; 36 | _ = alignment; 37 | _ = new_len; 38 | _ = ret_addr; 39 | @panic("out of memory"); 40 | } 41 | 42 | fn free(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, ret_addr: usize) void { 43 | _ = ctx; 44 | _ = buf; 45 | _ = buf_align; 46 | _ = ret_addr; 47 | @panic("out of memory"); 48 | } 49 | -------------------------------------------------------------------------------- /src/UnreachableAllocator.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const UnreachableAllocator = @This(); 3 | 4 | pub fn allocator() std.mem.Allocator { 5 | return .{ 6 | .ptr = undefined, 7 | .vtable = &.{ 8 | .alloc = alloc, 9 | .resize = resize, 10 | .remap = remap, 11 | .free = free, 12 | }, 13 | }; 14 | } 15 | 16 | fn alloc(ctx: *anyopaque, len: usize, ptr_align: std.mem.Alignment, ret_addr: usize) ?[*]u8 { 17 | _ = ctx; 18 | _ = len; 19 | _ = ptr_align; 20 | _ = ret_addr; 21 | unreachable; 22 | } 23 | 24 | fn resize(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, new_len: usize, ret_addr: usize) bool { 25 | _ = ctx; 26 | _ = buf; 27 | _ = buf_align; 28 | _ = new_len; 29 | _ = ret_addr; 30 | unreachable; 31 | } 32 | 33 | fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { 34 | _ = ctx; 35 | _ = memory; 36 | _ = alignment; 37 | _ = new_len; 38 | _ = ret_addr; 39 | unreachable; 40 | } 41 | 42 | fn free(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, ret_addr: usize) void { 43 | _ = ctx; 44 | _ = buf; 45 | _ = buf_align; 46 | _ = ret_addr; 47 | unreachable; 48 | } 49 | -------------------------------------------------------------------------------- /src/ZeroAllocator.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const ZeroAllocator = @This(); 3 | 4 | child_allocator: std.mem.Allocator, 5 | 6 | pub fn init(child_allocator: std.mem.Allocator) ZeroAllocator { 7 | return .{ 8 | .child_allocator = child_allocator, 9 | }; 10 | } 11 | 12 | pub fn allocator(self: *ZeroAllocator) std.mem.Allocator { 13 | return .{ 14 | .ptr = self, 15 | .vtable = &.{ 16 | .alloc = alloc, 17 | .resize = resize, 18 | .remap = remap, 19 | .free = free, 20 | }, 21 | }; 22 | } 23 | 24 | fn alloc(ctx: *anyopaque, len: usize, ptr_align: std.mem.Alignment, ret_addr: usize) ?[*]u8 { 25 | var self: *ZeroAllocator = @ptrCast(@alignCast(ctx)); 26 | const ptr = self.child_allocator.rawAlloc(len, ptr_align, ret_addr) orelse return null; 27 | @memset(ptr[0..len], 0); 28 | return ptr; 29 | } 30 | 31 | fn resize(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, new_len: usize, ret_addr: usize) bool { 32 | var self: *ZeroAllocator = @ptrCast(@alignCast(ctx)); 33 | const stable = self.child_allocator.rawResize(buf, buf_align, new_len, ret_addr); 34 | if (!stable) @memset(buf, 0); 35 | return stable; 36 | } 37 | 38 | fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { 39 | var self: *ZeroAllocator = @ptrCast(@alignCast(ctx)); 40 | const memory_new = self.child_allocator.rawRemap(memory, alignment, new_len, ret_addr) orelse return null; 41 | if (memory_new != memory.ptr) @memset(memory_new[0..new_len], 0); 42 | return memory_new; 43 | } 44 | 45 | fn free(ctx: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, ret_addr: usize) void { 46 | var self: *ZeroAllocator = @ptrCast(@alignCast(ctx)); 47 | return self.child_allocator.rawFree(buf, buf_align, ret_addr); 48 | } 49 | 50 | test ZeroAllocator { 51 | { 52 | const a = try std.testing.allocator.alloc(usize, 1); 53 | defer std.testing.allocator.free(a); 54 | try std.testing.expect(a[0] == undefined); 55 | } 56 | { 57 | var z = ZeroAllocator.init(std.testing.allocator); 58 | const l = z.allocator(); 59 | 60 | const a = try l.alloc(usize, 1); 61 | defer l.free(a); 62 | try std.testing.expect(a[0] == 0); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/lib.zig: -------------------------------------------------------------------------------- 1 | pub const FailingAllocator = @import("./FailingAllocator.zig"); 2 | pub const UnreachableAllocator = @import("./UnreachableAllocator.zig"); 3 | pub const PanicAllocator = @import("./PanicAllocator.zig"); 4 | pub const CountingAllocator = @import("./CountingAllocator.zig"); 5 | pub const LivenessAllocator = @import("./LivenessAllocator.zig"); 6 | pub const ZeroAllocator = @import("./ZeroAllocator.zig"); 7 | pub const LimitingAllocator = @import("./LimitingAllocator.zig"); 8 | -------------------------------------------------------------------------------- /test.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const gimme = @import("gimme"); 3 | 4 | test { 5 | std.testing.refAllDecls(gimme.FailingAllocator); 6 | std.testing.refAllDecls(gimme.CountingAllocator); 7 | std.testing.refAllDecls(gimme.LivenessAllocator); 8 | std.testing.refAllDecls(gimme.ZeroAllocator); 9 | } 10 | -------------------------------------------------------------------------------- /zigmod.yml: -------------------------------------------------------------------------------- 1 | id: ag273cr815rh1ncntvnjaf1x8rs1xl7v1v7r5aczudxa50nm 2 | name: gimme 3 | main: src/lib.zig 4 | license: MIT 5 | description: A yummy collection of useful Allocators 6 | min_zig_version: 0.11.0-dev.2912+d65b42e07 7 | --------------------------------------------------------------------------------