| ... | ... | @@ -3,11 +3,6 @@ const Allocator = std.mem.Allocator; |
| 3 | 3 | const Alignment = std.mem.Alignment; |
| 4 | 4 | const MemoryPool = std.heap.MemoryPool; |
| 5 | 5 | |
| 6 | | /// Deprecated. |
| 7 | | pub fn Managed(comptime Item: type) type { |
| 8 | | return ExtraManaged(Item, .{ .alignment = null }); |
| 9 | | } |
| 10 | | |
| 11 | 6 | /// A memory pool that can allocate objects of a single type very quickly. |
| 12 | 7 | /// Use this when you need to allocate a lot of objects of the same type, |
| 13 | 8 | /// because it outperforms general purpose allocators. |
| ... | ... | @@ -18,11 +13,6 @@ pub fn Aligned(comptime Item: type, comptime alignment: Alignment) type { |
| 18 | 13 | return Extra(Item, .{ .alignment = alignment }); |
| 19 | 14 | } |
| 20 | 15 | |
| 21 | | /// Deprecated. |
| 22 | | pub fn AlignedManaged(comptime Item: type, comptime alignment: Alignment) type { |
| 23 | | return ExtraManaged(Item, .{ .alignment = alignment }); |
| 24 | | } |
| 25 | | |
| 26 | 16 | pub const Options = struct { |
| 27 | 17 | /// The alignment of the memory pool items. Use `null` for natural alignment. |
| 28 | 18 | alignment: ?Alignment = null, |
| ... | ... | @@ -83,13 +73,6 @@ pub fn Extra(comptime Item: type, comptime pool_options: Options) type { |
| 83 | 73 | pool.* = undefined; |
| 84 | 74 | } |
| 85 | 75 | |
| 86 | | pub fn toManaged(pool: Pool, allocator: Allocator) ExtraManaged(Item, pool_options) { |
| 87 | | return .{ |
| 88 | | .allocator = allocator, |
| 89 | | .unmanaged = pool, |
| 90 | | }; |
| 91 | | } |
| 92 | | |
| 93 | 76 | /// Pre-allocates `num` items and adds them to the memory pool. |
| 94 | 77 | /// This allows at least `num` active allocations before an |
| 95 | 78 | /// `OutOfMemory` error might happen when calling `create()`. |
| ... | ... | @@ -114,7 +97,7 @@ pub fn Extra(comptime Item: type, comptime pool_options: Options) type { |
| 114 | 97 | /// NOTE: If `mode` is `free_all`, the function will always return `true`. |
| 115 | 98 | pub fn reset(pool: *Pool, allocator: Allocator, mode: ResetMode) bool { |
| 116 | 99 | // TODO: Potentially store all allocated objects in a list as well, allowing to |
| 117 | | // just move them into the free list instead of actually releasing the memory. |
| 100 | // just move them into the free list instead of actually releasing the memory. |
| 118 | 101 | |
| 119 | 102 | var arena = pool.arena_state.promote(allocator); |
| 120 | 103 | defer pool.arena_state = arena.state; |
| ... | ... | @@ -155,182 +138,56 @@ pub fn Extra(comptime Item: type, comptime pool_options: Options) type { |
| 155 | 138 | }; |
| 156 | 139 | } |
| 157 | 140 | |
| 158 | | /// Deprecated. |
| 159 | | pub fn ExtraManaged(comptime Item: type, comptime pool_options: Options) type { |
| 160 | | if (pool_options.alignment) |a| { |
| 161 | | if (a.compare(.eq, .of(Item))) { |
| 162 | | var new_options = pool_options; |
| 163 | | new_options.alignment = null; |
| 164 | | return ExtraManaged(Item, new_options); |
| 165 | | } |
| 166 | | } |
| 167 | | return struct { |
| 168 | | const Pool = @This(); |
| 169 | | |
| 170 | | allocator: Allocator, |
| 171 | | unmanaged: Unmanaged, |
| 172 | | |
| 173 | | pub const Unmanaged = Extra(Item, pool_options); |
| 174 | | pub const item_size = Unmanaged.item_size; |
| 175 | | pub const item_alignment = Unmanaged.item_alignment; |
| 176 | | |
| 177 | | const ItemPtr = Unmanaged.ItemPtr; |
| 178 | | |
| 179 | | /// Creates a new memory pool. |
| 180 | | pub fn init(allocator: Allocator) Pool { |
| 181 | | return Unmanaged.empty.toManaged(allocator); |
| 182 | | } |
| 183 | | |
| 184 | | /// Creates a new memory pool and pre-allocates `num` items. |
| 185 | | /// This allows up to `num` active allocations before an |
| 186 | | /// `OutOfMemory` error might happen when calling `create()`. |
| 187 | | pub fn initCapacity(allocator: Allocator, num: usize) Allocator.Error!Pool { |
| 188 | | return (try Unmanaged.initCapacity(allocator, num)).toManaged(allocator); |
| 189 | | } |
| 190 | | |
| 191 | | /// Destroys the memory pool and frees all allocated memory. |
| 192 | | pub fn deinit(pool: *Pool) void { |
| 193 | | pool.unmanaged.deinit(pool.allocator); |
| 194 | | pool.* = undefined; |
| 195 | | } |
| 196 | | |
| 197 | | /// Pre-allocates `num` items and adds them to the memory pool. |
| 198 | | /// This allows at least `num` active allocations before an |
| 199 | | /// `OutOfMemory` error might happen when calling `create()`. |
| 200 | | pub fn addCapacity(pool: *Pool, num: usize) Allocator.Error!void { |
| 201 | | return pool.unmanaged.addCapacity(pool.allocator, num); |
| 202 | | } |
| 203 | | |
| 204 | | pub const ResetMode = Unmanaged.ResetMode; |
| 205 | | |
| 206 | | /// Resets the memory pool and destroys all allocated items. |
| 207 | | /// This can be used to batch-destroy all objects without invalidating the memory pool. |
| 208 | | /// |
| 209 | | /// The function will return whether the reset operation was successful or not. |
| 210 | | /// If the reallocation failed `false` is returned. The pool will still be fully |
| 211 | | /// functional in that case, all memory is released. Future allocations just might |
| 212 | | /// be slower. |
| 213 | | /// |
| 214 | | /// NOTE: If `mode` is `free_all`, the function will always return `true`. |
| 215 | | pub fn reset(pool: *Pool, mode: ResetMode) bool { |
| 216 | | return pool.unmanaged.reset(pool.allocator, mode); |
| 217 | | } |
| 218 | | |
| 219 | | /// Creates a new item and adds it to the memory pool. |
| 220 | | pub fn create(pool: *Pool) Allocator.Error!ItemPtr { |
| 221 | | return pool.unmanaged.create(pool.allocator); |
| 222 | | } |
| 223 | | |
| 224 | | /// Destroys a previously created item. |
| 225 | | /// Only pass items to `ptr` that were previously created with `create()` of the same memory pool! |
| 226 | | pub fn destroy(pool: *Pool, ptr: ItemPtr) void { |
| 227 | | return pool.unmanaged.destroy(ptr); |
| 228 | | } |
| 229 | | |
| 230 | | fn allocNew(pool: *Pool) Allocator.Error!*align(item_alignment) [item_size]u8 { |
| 231 | | return pool.unmanaged.allocNew(pool.allocator); |
| 232 | | } |
| 233 | | }; |
| 234 | | } |
| 235 | | |
| 236 | 141 | test "basic" { |
| 237 | 142 | const a = std.testing.allocator; |
| 238 | 143 | |
| 239 | | { |
| 240 | | var pool: MemoryPool(u32) = .empty; |
| 241 | | defer pool.deinit(a); |
| 242 | | |
| 243 | | const p1 = try pool.create(a); |
| 244 | | const p2 = try pool.create(a); |
| 245 | | const p3 = try pool.create(a); |
| 246 | | |
| 247 | | // Assert uniqueness |
| 248 | | try std.testing.expect(p1 != p2); |
| 249 | | try std.testing.expect(p1 != p3); |
| 250 | | try std.testing.expect(p2 != p3); |
| 144 | var pool: MemoryPool(u32) = .empty; |
| 145 | defer pool.deinit(a); |
| 251 | 146 | |
| 252 | | pool.destroy(p2); |
| 253 | | const p4 = try pool.create(a); |
| 254 | | |
| 255 | | // Assert memory reuse |
| 256 | | try std.testing.expect(p2 == p4); |
| 257 | | } |
| 147 | const p1 = try pool.create(a); |
| 148 | const p2 = try pool.create(a); |
| 149 | const p3 = try pool.create(a); |
| 258 | 150 | |
| 259 | | { |
| 260 | | var pool: Managed(u32) = .init(std.testing.allocator); |
| 261 | | defer pool.deinit(); |
| 151 | // Assert uniqueness |
| 152 | try std.testing.expect(p1 != p2); |
| 153 | try std.testing.expect(p1 != p3); |
| 154 | try std.testing.expect(p2 != p3); |
| 262 | 155 | |
| 263 | | const p1 = try pool.create(); |
| 264 | | const p2 = try pool.create(); |
| 265 | | const p3 = try pool.create(); |
| 156 | pool.destroy(p2); |
| 157 | const p4 = try pool.create(a); |
| 266 | 158 | |
| 267 | | // Assert uniqueness |
| 268 | | try std.testing.expect(p1 != p2); |
| 269 | | try std.testing.expect(p1 != p3); |
| 270 | | try std.testing.expect(p2 != p3); |
| 271 | | |
| 272 | | pool.destroy(p2); |
| 273 | | const p4 = try pool.create(); |
| 274 | | |
| 275 | | // Assert memory reuse |
| 276 | | try std.testing.expect(p2 == p4); |
| 277 | | } |
| 159 | // Assert memory reuse |
| 160 | try std.testing.expect(p2 == p4); |
| 278 | 161 | } |
| 279 | 162 | |
| 280 | 163 | test "initCapacity (success)" { |
| 281 | 164 | const a = std.testing.allocator; |
| 282 | 165 | |
| 283 | | { |
| 284 | | var pool: MemoryPool(u32) = try .initCapacity(a, 4); |
| 285 | | defer pool.deinit(a); |
| 286 | | |
| 287 | | _ = try pool.create(a); |
| 288 | | _ = try pool.create(a); |
| 289 | | _ = try pool.create(a); |
| 290 | | } |
| 291 | | |
| 292 | | { |
| 293 | | var pool: Managed(u32) = try .initCapacity(a, 4); |
| 294 | | defer pool.deinit(); |
| 166 | var pool: MemoryPool(u32) = try .initCapacity(a, 4); |
| 167 | defer pool.deinit(a); |
| 295 | 168 | |
| 296 | | _ = try pool.create(); |
| 297 | | _ = try pool.create(); |
| 298 | | _ = try pool.create(); |
| 299 | | } |
| 169 | _ = try pool.create(a); |
| 170 | _ = try pool.create(a); |
| 171 | _ = try pool.create(a); |
| 300 | 172 | } |
| 301 | 173 | |
| 302 | 174 | test "initCapacity (failure)" { |
| 303 | 175 | const failer = std.testing.failing_allocator; |
| 304 | 176 | try std.testing.expectError(error.OutOfMemory, MemoryPool(u32).initCapacity(failer, 5)); |
| 305 | | try std.testing.expectError(error.OutOfMemory, Managed(u32).initCapacity(failer, 5)); |
| 306 | 177 | } |
| 307 | 178 | |
| 308 | 179 | test "growable" { |
| 309 | 180 | const a = std.testing.allocator; |
| 310 | 181 | |
| 311 | | { |
| 312 | | var pool: Extra(u32, .{ .growable = false }) = try .initCapacity(a, 4); |
| 313 | | defer pool.deinit(a); |
| 314 | | |
| 315 | | _ = try pool.create(a); |
| 316 | | _ = try pool.create(a); |
| 317 | | _ = try pool.create(a); |
| 318 | | _ = try pool.create(a); |
| 319 | | |
| 320 | | try std.testing.expectError(error.OutOfMemory, pool.create(a)); |
| 321 | | } |
| 322 | | |
| 323 | | { |
| 324 | | var pool: ExtraManaged(u32, .{ .growable = false }) = try .initCapacity(a, 4); |
| 325 | | defer pool.deinit(); |
| 182 | var pool: Extra(u32, .{ .growable = false }) = try .initCapacity(a, 4); |
| 183 | defer pool.deinit(a); |
| 326 | 184 | |
| 327 | | _ = try pool.create(); |
| 328 | | _ = try pool.create(); |
| 329 | | _ = try pool.create(); |
| 330 | | _ = try pool.create(); |
| 185 | _ = try pool.create(a); |
| 186 | _ = try pool.create(a); |
| 187 | _ = try pool.create(a); |
| 188 | _ = try pool.create(a); |
| 331 | 189 | |
| 332 | | try std.testing.expectError(error.OutOfMemory, pool.create()); |
| 333 | | } |
| 190 | try std.testing.expectError(error.OutOfMemory, pool.create(a)); |
| 334 | 191 | } |
| 335 | 192 | |
| 336 | 193 | test "greater than pointer default alignment" { |
| ... | ... | @@ -339,21 +196,11 @@ test "greater than pointer default alignment" { |
| 339 | 196 | }; |
| 340 | 197 | const a = std.testing.allocator; |
| 341 | 198 | |
| 342 | | { |
| 343 | | var pool: MemoryPool(Foo) = .empty; |
| 344 | | defer pool.deinit(a); |
| 345 | | |
| 346 | | const foo: *Foo = try pool.create(a); |
| 347 | | pool.destroy(foo); |
| 348 | | } |
| 349 | | |
| 350 | | { |
| 351 | | var pool: Managed(Foo) = .init(a); |
| 352 | | defer pool.deinit(); |
| 199 | var pool: MemoryPool(Foo) = .empty; |
| 200 | defer pool.deinit(a); |
| 353 | 201 | |
| 354 | | const foo: *Foo = try pool.create(); |
| 355 | | pool.destroy(foo); |
| 356 | | } |
| 202 | const foo: *Foo = try pool.create(a); |
| 203 | pool.destroy(foo); |
| 357 | 204 | } |
| 358 | 205 | |
| 359 | 206 | test "greater than pointer manual alignment" { |
| ... | ... | @@ -362,19 +209,9 @@ test "greater than pointer manual alignment" { |
| 362 | 209 | }; |
| 363 | 210 | const a = std.testing.allocator; |
| 364 | 211 | |
| 365 | | { |
| 366 | | var pool: Aligned(Foo, .@"16") = .empty; |
| 367 | | defer pool.deinit(a); |
| 368 | | |
| 369 | | const foo: *align(16) Foo = try pool.create(a); |
| 370 | | pool.destroy(foo); |
| 371 | | } |
| 212 | var pool: Aligned(Foo, .@"16") = .empty; |
| 213 | defer pool.deinit(a); |
| 372 | 214 | |
| 373 | | { |
| 374 | | var pool: AlignedManaged(Foo, .@"16") = .init(a); |
| 375 | | defer pool.deinit(); |
| 376 | | |
| 377 | | const foo: *align(16) Foo = try pool.create(); |
| 378 | | pool.destroy(foo); |
| 379 | | } |
| 215 | const foo: *align(16) Foo = try pool.create(a); |
| 216 | pool.destroy(foo); |
| 380 | 217 | } |