| 1 | /// A memory pool lets you reuse shared objects. When objects are released, |
| 2 | /// their state is not reset. This allows you to use it with objects such |
| 3 | /// as ArenaAllocator. |
| 4 | fn ObjectPool(Item: type, static_count: usize, dynamic_limit: usize, deinitFunction: anytype) type { |
| 5 | return struct { |
| 6 | static_data: [static_count]Item, |
| 7 | static_usage: std.StaticBitSet(static_count), |
| 8 | dynamic_unused: if (have_dynamic_pool) LinkedList else void, |
| 9 | dynamic_count: if (have_dynamic_limit) std.math.IntFittingRange(0, dynamic_limit) else void, |
| 10 | |
| 11 | const have_dynamic_pool = dynamic_limit > 0; |
| 12 | const have_dynamic_limit = have_dynamic_pool and dynamic_limit < std.math.maxInt(usize); |
| 13 | |
| 14 | const LinkedList = std.SinglyLinkedList(Item); |
| 15 | |
| 16 | const empty: @This() = .{ |
| 17 | .static_data = undefined, |
| 18 | .static_usage = .initFull(), |
| 19 | .dynamic_unused = .{}, |
| 20 | }; |
| 21 | |
| 22 | pub fn get(pool: *@This(), allocator: std.mem.Allocator) !*Item { |
| 23 | if (pool.static_usage.toggleFirstSet()) |index| { |
| 24 | return &pool.static_data[index]; |
| 25 | } |
| 26 | if (have_dynamic_pool) { |
| 27 | if (pool.dynamic_unused.popFirst()) |first| { |
| 28 | if (have_dynamic_limit) |
| 29 | pool.dynamic_count -= 1; |
| 30 | return &first.data; |
| 31 | } |
| 32 | } |
| 33 | return &(try allocator.create(LinkedList.Node)).data; |
| 34 | } |
| 35 | |
| 36 | pub fn release(pool: *@This(), item: *Item, allocator: std.mem.Allocator) void { |
| 37 | deinitFunction(item); |
| 38 | _ = pool; // autofix |
| 39 | _ = allocator; // autofix |
| 40 | // |
| 41 | } |
| 42 | }; |
| 43 | } |
| 44 | |
| 45 | fn AutoObjectPool(Item: type) type { |
| 46 | return ObjectPool(Item, 32, 32, Item.deinit); |
| 47 | } |
| 48 | |
| 49 | const std = @import("std"); |