const std = @import("std");
fn demonstrate_general_purpose_allocator() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const memory = try allocator.alloc(u8, 1024);
defer allocator.free(memory);
@memset(memory, 0);
std.debug.print("Allocated {} bytes with GPA\n", .{memory.len});
}
fn demonstrate_page_allocator() !void {
const allocator = std.heap.page_allocator;
const large_block = try allocator.alloc(u8, 4096);
defer allocator.free(large_block);
std.debug.print("Allocated {} bytes with page allocator\n", .{large_block.len});
}
fn demonstrate_fixed_buffer_allocator() !void {
var buffer: [1024]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();
const data = try allocator.alloc(u8, 256);
defer allocator.free(data);
@memset(data, 'A');
std.debug.print("FBA used: {} / {} bytes\n", .{ fba.end_index, buffer.len });
}
fn demonstrate_c_allocator() void {
const allocator = std.heap.c_allocator;
const ptr = allocator.alloc(u8, 512) catch unreachable;
defer allocator.free(ptr);
std.debug.print("Allocated {} bytes with C allocator\n", .{ptr.len});
}
fn demonstrate_arena_allocator() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
var arena = std.heap.ArenaAllocator.init(gpa.allocator());
defer arena.deinit();
const allocator = arena.allocator();
_ = try allocator.alloc(u8, 100);
_ = try allocator.alloc(u8, 200);
_ = try allocator.alloc(u8, 300);
std.debug.print("Arena allocated 3 blocks, freed all at once\n", .{});
}
const PoolAllocator = struct {
const Self = @This();
allocator: std.mem.Allocator,
block_size: usize,
blocks_per_chunk: usize,
free_list: ?*Block,
chunks: std.ArrayList([]u8),
const Block = struct {
next: ?*Block,
};
fn init(backing_allocator: std.mem.Allocator, block_size: usize, blocks_per_chunk: usize) Self {
return .{
.allocator = backing_allocator,
.block_size = @max(block_size, @sizeOf(Block)),
.blocks_per_chunk = blocks_per_chunk,
.free_list = null,
.chunks = std.ArrayList([]u8).init(backing_allocator),
};
}
fn deinit(self: *Self) void {
for (self.chunks.items) |chunk| {
self.allocator.free(chunk);
}
self.chunks.deinit();
}
fn allocBlock(self: *Self) !*anyopaque {
if (self.free_list == null) {
try self.grow();
}
const block = self.free_list.?;
self.free_list = block.next;
return block;
}
fn freeBlock(self: *Self, ptr: *anyopaque) void {
const block: *Block = @ptrCast(@alignCast(ptr));
block.next = self.free_list;
self.free_list = block;
}
fn grow(self: *Self) !void {
const chunk_size = self.block_size * self.blocks_per_chunk;
const chunk = try self.allocator.alloc(u8, chunk_size);
try self.chunks.append(chunk);
var i: usize = 0;
while (i < self.blocks_per_chunk) : (i += 1) {
const offset = i * self.block_size;
const block: *Block = @ptrCast(@alignCast(&chunk[offset]));
block.next = self.free_list;
self.free_list = block;
}
}
};
test "pool allocator" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
var pool = PoolAllocator.init(gpa.allocator(), 64, 10);
defer pool.deinit();
const obj1 = try pool.allocBlock();
const obj2 = try pool.allocBlock();
std.debug.print("Pool allocated objects: {} and {}\n", .{ obj1, obj2 });
pool.freeBlock(obj1);
pool.freeBlock(obj2);
}
const StackAllocator = struct {
const Self = @This();
allocator: std.mem.Allocator,
buffer: []u8,
offset: usize,
marks: std.ArrayList(usize),
fn init(backing_allocator: std.mem.Allocator, size: usize) !Self {
const buffer = try backing_allocator.alloc(u8, size);
return .{
.allocator = backing_allocator,
.buffer = buffer,
.offset = 0,
.marks = std.ArrayList(usize).init(backing_allocator),
};
}
fn deinit(self: *Self) void {
self.allocator.free(self.buffer);
self.marks.deinit();
}
fn pushMark(self: *Self) !void {
try self.marks.append(self.offset);
}
fn popMark(self: *Self) void {
if (self.marks.items.len > 0) {
self.offset = self.marks.pop();
}
}
fn alloc(self: *Self, size: usize, alignment: usize) ?[]u8 {
const aligned_offset = std.mem.alignForward(usize, self.offset, alignment);
const new_offset = aligned_offset + size;
if (new_offset > self.buffer.len) return null;
const result = self.buffer[aligned_offset..new_offset];
self.offset = new_offset;
return result;
}
fn reset(self: *Self) void {
self.offset = 0;
self.marks.clearAndFree();
}
};
test "stack allocator" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
var stack = try StackAllocator.init(gpa.allocator(), 1024);
defer stack.deinit();
try stack.pushMark();
const mem1 = stack.alloc(100, 8).?;
const mem2 = stack.alloc(200, 8).?;
std.debug.print("Stack alloc: {} bytes and {} bytes\n", .{ mem1.len, mem2.len });
stack.popMark();
const mem3 = stack.alloc(150, 8).?;
std.debug.print("After pop: {} bytes\n", .{mem3.len});
}
fn aligned_alloc_example() !void {
const allocator = std.heap.page_allocator;
const alignment: usize = 64;
const size: usize = 256;
const ptr = try allocator.allocAdvanced(u8, alignment, size, .exact);
defer allocator.free(ptr);
const addr = @intFromPtr(ptr.ptr);
std.debug.print("Aligned allocation at 0x{x} (aligned to {}): {}\n", .{ addr, alignment, addr % alignment == 0 });
}
fn realloc_example() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var buf = try allocator.alloc(u8, 16);
defer allocator.free(buf);
@memcpy(buf[0..5], "Hello");
std.debug.print("Original: {s}\n", .{buf[0..5]});
buf = try allocator.realloc(buf, 32);
@memcpy(buf[5..11], " World");
std.debug.print("Reallocated: {s}\n", .{buf[0..11]});
}
fn allocator_vtable_example() !void {
const MyAlloc = struct {
counter: usize = 0,
fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
_ = ret_addr;
const self: *@This() = @ptrCast(@alignCast(ctx));
self.counter += 1;
_ = ptr_align;
_ = len;
return null;
}
fn resize(ctx: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool {
_ = ctx;
_ = buf;
_ = buf_align;
_ = new_len;
_ = ret_addr;
return false;
}
fn free(ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void {
_ = ctx;
_ = buf;
_ = buf_align;
_ = ret_addr;
}
};
var my_alloc: MyAlloc = .{};
_ = my_alloc;
}
pub fn main() !void {
std.debug.print("=== General Purpose Allocator ===\n", .{});
try demonstrate_general_purpose_allocator();
std.debug.print("\n=== Page Allocator ===\n", .{});
try demonstrate_page_allocator();
std.debug.print("\n=== Fixed Buffer Allocator ===\n", .{});
demonstrate_fixed_buffer_allocator();
std.debug.print("\n=== Arena Allocator ===\n", .{});
try demonstrate_arena_allocator();
std.debug.print("\n=== Realloc Example ===\n", .{});
try realloc_example();
}