| 
   | 
const std = @import("../std.zig");
const debug_mode = @import("builtin").mode == .Debug;
 | 
| MemoryPoolError | 
pub const MemoryPoolError = error{OutOfMemory};
 | 
| MemoryPool()A memory pool that can allocate objects of a single type very quickly. Use this when you need to allocate a lot of objects of the same type, because It outperforms general purpose allocators. | 
pub fn MemoryPool(comptime Item: type) type {
    return MemoryPoolAligned(Item, @alignOf(Item));
}
 | 
| MemoryPoolAligned()A memory pool that can allocate objects of a single type very quickly. Use this when you need to allocate a lot of objects of the same type, because It outperforms general purpose allocators. | 
pub fn MemoryPoolAligned(comptime Item: type, comptime alignment: u29) type {
    if (@alignOf(Item) == alignment) {
        return MemoryPoolExtra(Item, .{});
    } else {
        return MemoryPoolExtra(Item, .{ .alignment = alignment });
    }
}
 | 
| Options | 
pub const Options = struct {
    alignment: ?u29 = null,
    growable: bool = true,
};
 | 
| MemoryPoolExtra() The alignment of the memory pool items. Use  | 
pub fn MemoryPoolExtra(comptime Item: type, comptime pool_options: Options) type {
    return struct {
        const Pool = @This();
        pub const item_size = @max(@sizeOf(Node), @sizeOf(Item));
        pub const item_alignment = @max(@alignOf(Node), pool_options.alignment orelse 0);
        const Node = struct {
            next: ?*@This(),
        };
        const NodePtr = *align(item_alignment) Node;
        const ItemPtr = *align(item_alignment) Item;
        arena: std.heap.ArenaAllocator,
        free_list: ?NodePtr = null,
 | 
| init() Size of the memory pool items. This is not necessarily the same as  | 
        pub fn init(allocator: std.mem.Allocator) Pool {
            return .{ .arena = std.heap.ArenaAllocator.init(allocator) };
        }
 | 
| initPreheated() Creates a new memory pool and pre-allocates  | 
        pub fn initPreheated(allocator: std.mem.Allocator, initial_size: usize) MemoryPoolError!Pool {
            var pool = init(allocator);
            errdefer pool.deinit();
            var i: usize = 0;
            while (i < initial_size) : (i += 1) {
                const raw_mem = try pool.allocNew();
                const free_node = @as(NodePtr, @ptrCast(raw_mem));
                free_node.* = Node{
                    .next = pool.free_list,
                };
                pool.free_list = free_node;
            }
            return pool;
        }
 | 
| deinit()Destroys the memory pool and frees all allocated memory. | 
        pub fn deinit(pool: *Pool) void {
            pool.arena.deinit();
            pool.* = undefined;
        }
        pub const ResetMode = std.heap.ArenaAllocator.ResetMode;
 | 
| reset() Resets the memory pool and destroys all allocated items. This can be used to batch-destroy all objects without invalidating the memory pool. | 
        pub fn reset(pool: *Pool, mode: ResetMode) bool {
            // TODO: Potentially store all allocated objects in a list as well, allowing to
            //       just move them into the free list instead of actually releasing the memory.
            const reset_successful = pool.arena.reset(mode);
            pool.free_list = null;
            return reset_successful;
        }
 | 
| create()Creates a new item and adds it to the memory pool. | 
        pub fn create(pool: *Pool) !ItemPtr {
            const node = if (pool.free_list) |item| blk: {
                pool.free_list = item.next;
                break :blk item;
            } else if (pool_options.growable)
                @as(NodePtr, @ptrCast(try pool.allocNew()))
            else
                return error.OutOfMemory;
            const ptr = @as(ItemPtr, @ptrCast(node));
            ptr.* = undefined;
            return ptr;
        }
 | 
| destroy() Destroys a previously created item. Only pass items to  | 
        pub fn destroy(pool: *Pool, ptr: ItemPtr) void {
            ptr.* = undefined;
            const node = @as(NodePtr, @ptrCast(ptr));
            node.* = Node{
                .next = pool.free_list,
            };
            pool.free_list = node;
        }
        fn allocNew(pool: *Pool) MemoryPoolError!*align(item_alignment) [item_size]u8 {
            const mem = try pool.arena.allocator().alignedAlloc(u8, item_alignment, item_size);
            return mem[0..item_size]; // coerce slice to array pointer
        }
    };
}
 | 
| Test:memory pool: basic | 
test "memory pool: basic" {
    var pool = MemoryPool(u32).init(std.testing.allocator);
    defer pool.deinit();
    const p1 = try pool.create();
    const p2 = try pool.create();
    const p3 = try pool.create();
    // Assert uniqueness
    try std.testing.expect(p1 != p2);
    try std.testing.expect(p1 != p3);
    try std.testing.expect(p2 != p3);
    pool.destroy(p2);
    const p4 = try pool.create();
    // Assert memory reuse
    try std.testing.expect(p2 == p4);
}
 | 
| Test:memory pool: preheating (success) | 
test "memory pool: preheating (success)" {
    var pool = try MemoryPool(u32).initPreheated(std.testing.allocator, 4);
    defer pool.deinit();
    _ = try pool.create();
    _ = try pool.create();
    _ = try pool.create();
}
 | 
| Test:memory pool: preheating (failure) | 
test "memory pool: preheating (failure)" {
    var failer = std.testing.failing_allocator;
    try std.testing.expectError(error.OutOfMemory, MemoryPool(u32).initPreheated(failer, 5));
}
 | 
| Test:memory pool: growable | 
test "memory pool: growable" {
    var pool = try MemoryPoolExtra(u32, .{ .growable = false }).initPreheated(std.testing.allocator, 4);
    defer pool.deinit();
    _ = try pool.create();
    _ = try pool.create();
    _ = try pool.create();
    _ = try pool.create();
    try std.testing.expectError(error.OutOfMemory, pool.create());
}
 | 
| Generated by zstd-browse2 on 2023-11-04 14:12:28 -0400. |