zig-alloc - 解答
実装コード
FixedBufferAllocator
const std = @import("std");
pub const FixedBufferAllocator = struct {
buffer: []u8,
end_index: usize = 0,
pub fn init(buffer: []u8) FixedBufferAllocator {
return .{ .buffer = buffer };
}
pub fn allocator(self: *FixedBufferAllocator) std.mem.Allocator {
return .{
.ptr = self,
.vtable = &.{
.alloc = alloc,
.resize = resize,
.free = free,
},
};
}
fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
_ = ret_addr;
const self: *FixedBufferAllocator = @ptrCast(@alignCast(ctx));
const alignment = @as(usize, 1) << @intCast(ptr_align);
// アライメント調整
const aligned_index = std.mem.alignForward(usize, self.end_index, alignment);
if (aligned_index + len > self.buffer.len) {
return null; // 容量不足
}
const result = self.buffer.ptr + aligned_index;
self.end_index = aligned_index + len;
return result;
}
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;
// 固定バッファアロケータでは個別解放しない
}
pub fn reset(self: *FixedBufferAllocator) void {
self.end_index = 0;
}
};
ArenaAllocator
pub const ArenaAllocator = struct {
child_allocator: std.mem.Allocator,
state: State,
const BufNode = struct {
data: []u8,
next: ?*BufNode,
};
const State = struct {
buffer_list: ?*BufNode = null,
end_index: usize = 0,
};
pub fn init(child_allocator: std.mem.Allocator) ArenaAllocator {
return .{
.child_allocator = child_allocator,
.state = .{},
};
}
pub fn deinit(self: *ArenaAllocator) void {
var node = self.state.buffer_list;
while (node) |n| {
const next = n.next;
self.child_allocator.free(n.data);
self.child_allocator.destroy(n);
node = next;
}
self.state = .{};
}
pub fn allocator(self: *ArenaAllocator) std.mem.Allocator {
return .{
.ptr = self,
.vtable = &.{
.alloc = alloc,
.resize = resize,
.free = free,
},
};
}
fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
_ = ret_addr;
const self: *ArenaAllocator = @ptrCast(@alignCast(ctx));
// 新しいバッファを確保
const buf_len = @max(len, 4096);
const buf = self.child_allocator.alloc(u8, buf_len) catch return null;
// ノードを作成
const node = self.child_allocator.create(BufNode) catch {
self.child_allocator.free(buf);
return null;
};
node.* = .{
.data = buf,
.next = self.state.buffer_list,
};
self.state.buffer_list = node;
const alignment = @as(usize, 1) << @intCast(ptr_align);
const aligned_addr = std.mem.alignForward(usize, @intFromPtr(buf.ptr), alignment);
return @ptrFromInt(aligned_addr);
}
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;
// アリーナでは個別解放しない
}
};
LoggingAllocator
pub fn LoggingAllocator(comptime Writer: type) type {
return struct {
child_allocator: std.mem.Allocator,
writer: Writer,
total_allocated: usize = 0,
allocation_count: usize = 0,
const Self = @This();
pub fn init(child_allocator: std.mem.Allocator, writer: Writer) Self {
return .{
.child_allocator = child_allocator,
.writer = writer,
};
}
pub fn allocator(self: *Self) std.mem.Allocator {
return .{
.ptr = self,
.vtable = &.{
.alloc = alloc,
.resize = resize,
.free = free,
},
};
}
fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
const self: *Self = @ptrCast(@alignCast(ctx));
const result = self.child_allocator.rawAlloc(len, ptr_align, ret_addr);
self.writer.print("ALLOC: {} bytes, align={}, result={?}\n", .{
len,
@as(usize, 1) << @intCast(ptr_align),
result,
}) catch {};
if (result != null) {
self.total_allocated += len;
self.allocation_count += 1;
}
return result;
}
fn resize(ctx: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool {
const self: *Self = @ptrCast(@alignCast(ctx));
const result = self.child_allocator.rawResize(buf, buf_align, new_len, ret_addr);
self.writer.print("RESIZE: {} -> {} bytes, success={}\n", .{
buf.len,
new_len,
result,
}) catch {};
return result;
}
fn free(ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void {
const self: *Self = @ptrCast(@alignCast(ctx));
self.writer.print("FREE: {} bytes at {*}\n", .{
buf.len,
buf.ptr,
}) catch {};
self.total_allocated -= buf.len;
self.child_allocator.rawFree(buf, buf_align, ret_addr);
}
pub fn printStats(self: *Self) void {
self.writer.print("=== Allocator Stats ===\n", .{}) catch {};
self.writer.print("Total allocated: {} bytes\n", .{self.total_allocated}) catch {};
self.writer.print("Allocation count: {}\n", .{self.allocation_count}) catch {};
}
};
}
テストコード
const testing = std.testing;
test "FixedBufferAllocator basic" {
var buffer: [1024]u8 = undefined;
var fba = FixedBufferAllocator.init(&buffer);
const alloc = fba.allocator();
const slice1 = try alloc.alloc(u8, 100);
try testing.expect(slice1.len == 100);
const slice2 = try alloc.alloc(u8, 200);
try testing.expect(slice2.len == 200);
// バッファをリセット
fba.reset();
const slice3 = try alloc.alloc(u8, 500);
try testing.expect(slice3.len == 500);
}
test "ArenaAllocator basic" {
var arena = ArenaAllocator.init(testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
const slice1 = try alloc.alloc(u8, 1000);
try testing.expect(slice1.len == 1000);
const slice2 = try alloc.alloc(u32, 100);
try testing.expect(slice2.len == 100);
}