| ... | @@ -61,16 +61,16 @@ const Allocator = std.mem.Allocator; | ... | @@ -61,16 +61,16 @@ const Allocator = std.mem.Allocator; |
| 61 | // shelf_size = prealloc * 2 ** (shelf_index + 1) | 61 | // shelf_size = prealloc * 2 ** (shelf_index + 1) |
| 62 | | 62 | |
| 63 | /// This is a stack data structure where pointers to indexes have the same lifetime as the data structure | 63 | /// This is a stack data structure where pointers to indexes have the same lifetime as the data structure |
| 64 | /// itself, unlike ArrayList where push() invalidates all existing element pointers. | 64 | /// itself, unlike ArrayList where append() invalidates all existing element pointers. |
| 65 | /// The tradeoff is that elements are not guaranteed to be contiguous. For that, use ArrayList. | 65 | /// The tradeoff is that elements are not guaranteed to be contiguous. For that, use ArrayList. |
| 66 | /// Note however that most elements are contiguous, making this data structure cache-friendly. | 66 | /// Note however that most elements are contiguous, making this data structure cache-friendly. |
| 67 | /// | 67 | /// |
| 68 | /// Because it never has to copy elements from an old location to a new location, it does not require | 68 | /// Because it never has to copy elements from an old location to a new location, it does not require |
| 69 | /// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator. | 69 | /// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator. |
| 70 | /// Note that the push() and pop() convenience methods perform a copy, but you can instead use | 70 | /// Note that the append() and pop() convenience methods perform a copy, but you can instead use |
| 71 | /// addOne(), at(), setCapacity(), and shrinkCapacity() to avoid copying items. | 71 | /// addOne(), at(), setCapacity(), and shrinkCapacity() to avoid copying items. |
| 72 | /// | 72 | /// |
| 73 | /// This data structure has O(1) push and O(1) pop. | 73 | /// This data structure has O(1) append and O(1) pop. |
| 74 | /// | 74 | /// |
| 75 | /// It supports preallocated elements, making it especially well suited when the expected maximum | 75 | /// It supports preallocated elements, making it especially well suited when the expected maximum |
| 76 | /// size is small. `prealloc_item_count` must be 0, or a power of 2. | 76 | /// size is small. `prealloc_item_count` must be 0, or a power of 2. |
| ... | @@ -91,10 +91,9 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type | ... | @@ -91,10 +91,9 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 91 | } | 91 | } |
| 92 | }; | 92 | }; |
| 93 | | 93 | |
| 94 | prealloc_segment: [prealloc_item_count]T, | 94 | prealloc_segment: [prealloc_item_count]T = undefined, |
| 95 | dynamic_segments: [][*]T, | 95 | dynamic_segments: [][*]T = &[_][*]T{}, |
| 96 | allocator: Allocator, | 96 | len: usize = 0, |
| 97 | len: usize, | | |
| 98 | | 97 | |
| 99 | pub const prealloc_count = prealloc_item_count; | 98 | pub const prealloc_count = prealloc_item_count; |
| 100 | | 99 | |
| ... | @@ -106,19 +105,9 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type | ... | @@ -106,19 +105,9 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 106 | } | 105 | } |
| 107 | } | 106 | } |
| 108 | | 107 | |
| 109 | /// Deinitialize with `deinit` | 108 | pub fn deinit(self: *Self, allocator: Allocator) void { |
| 110 | pub fn init(allocator: Allocator) Self { | 109 | self.freeShelves(allocator, @intCast(ShelfIndex, self.dynamic_segments.len), 0); |
| 111 | return Self{ | 110 | allocator.free(self.dynamic_segments); |
| 112 | .allocator = allocator, | | |
| 113 | .len = 0, | | |
| 114 | .prealloc_segment = undefined, | | |
| 115 | .dynamic_segments = &[_][*]T{}, | | |
| 116 | }; | | |
| 117 | } | | |
| 118 | | | |
| 119 | pub fn deinit(self: *Self) void { | | |
| 120 | self.freeShelves(@intCast(ShelfIndex, self.dynamic_segments.len), 0); | | |
| 121 | self.allocator.free(self.dynamic_segments); | | |
| 122 | self.* = undefined; | 111 | self.* = undefined; |
| 123 | } | 112 | } |
| 124 | | 113 | |
| ... | @@ -131,14 +120,14 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type | ... | @@ -131,14 +120,14 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 131 | return self.len; | 120 | return self.len; |
| 132 | } | 121 | } |
| 133 | | 122 | |
| 134 | pub fn push(self: *Self, item: T) !void { | 123 | pub fn append(self: *Self, allocator: Allocator, item: T) Allocator.Error!void { |
| 135 | const new_item_ptr = try self.addOne(); | 124 | const new_item_ptr = try self.addOne(allocator); |
| 136 | new_item_ptr.* = item; | 125 | new_item_ptr.* = item; |
| 137 | } | 126 | } |
| 138 | | 127 | |
| 139 | pub fn pushMany(self: *Self, items: []const T) !void { | 128 | pub fn appendSlice(self: *Self, allocator: Allocator, items: []const T) Allocator.Error!void { |
| 140 | for (items) |item| { | 129 | for (items) |item| { |
| 141 | try self.push(item); | 130 | try self.append(allocator, item); |
| 142 | } | 131 | } |
| 143 | } | 132 | } |
| 144 | | 133 | |
| ... | @@ -151,47 +140,48 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type | ... | @@ -151,47 +140,48 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 151 | return result; | 140 | return result; |
| 152 | } | 141 | } |
| 153 | | 142 | |
| 154 | pub fn addOne(self: *Self) !*T { | 143 | pub fn addOne(self: *Self, allocator: Allocator) Allocator.Error!*T { |
| 155 | const new_length = self.len + 1; | 144 | const new_length = self.len + 1; |
| 156 | try self.growCapacity(new_length); | 145 | try self.growCapacity(allocator, new_length); |
| 157 | const result = uncheckedAt(self, self.len); | 146 | const result = uncheckedAt(self, self.len); |
| 158 | self.len = new_length; | 147 | self.len = new_length; |
| 159 | return result; | 148 | return result; |
| 160 | } | 149 | } |
| 161 | | 150 | |
| 162 | /// Grows or shrinks capacity to match usage. | 151 | /// Grows or shrinks capacity to match usage. |
| 163 | pub fn setCapacity(self: *Self, new_capacity: usize) !void { | 152 | /// TODO update this and related methods to match the conventions set by ArrayList |
| | 153 | pub fn setCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void { |
| 164 | if (prealloc_item_count != 0) { | 154 | if (prealloc_item_count != 0) { |
| 165 | if (new_capacity <= @as(usize, 1) << (prealloc_exp + @intCast(ShelfIndex, self.dynamic_segments.len))) { | 155 | if (new_capacity <= @as(usize, 1) << (prealloc_exp + @intCast(ShelfIndex, self.dynamic_segments.len))) { |
| 166 | return self.shrinkCapacity(new_capacity); | 156 | return self.shrinkCapacity(allocator, new_capacity); |
| 167 | } | 157 | } |
| 168 | } | 158 | } |
| 169 | return self.growCapacity(new_capacity); | 159 | return self.growCapacity(allocator, new_capacity); |
| 170 | } | 160 | } |
| 171 | | 161 | |
| 172 | /// Only grows capacity, or retains current capacity | 162 | /// Only grows capacity, or retains current capacity |
| 173 | pub fn growCapacity(self: *Self, new_capacity: usize) !void { | 163 | pub fn growCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void { |
| 174 | const new_cap_shelf_count = shelfCount(new_capacity); | 164 | const new_cap_shelf_count = shelfCount(new_capacity); |
| 175 | const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); | 165 | const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); |
| 176 | if (new_cap_shelf_count > old_shelf_count) { | 166 | if (new_cap_shelf_count > old_shelf_count) { |
| 177 | self.dynamic_segments = try self.allocator.realloc(self.dynamic_segments, new_cap_shelf_count); | 167 | self.dynamic_segments = try allocator.realloc(self.dynamic_segments, new_cap_shelf_count); |
| 178 | var i = old_shelf_count; | 168 | var i = old_shelf_count; |
| 179 | errdefer { | 169 | errdefer { |
| 180 | self.freeShelves(i, old_shelf_count); | 170 | self.freeShelves(allocator, i, old_shelf_count); |
| 181 | self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, old_shelf_count); | 171 | self.dynamic_segments = allocator.shrink(self.dynamic_segments, old_shelf_count); |
| 182 | } | 172 | } |
| 183 | while (i < new_cap_shelf_count) : (i += 1) { | 173 | while (i < new_cap_shelf_count) : (i += 1) { |
| 184 | self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; | 174 | self.dynamic_segments[i] = (try allocator.alloc(T, shelfSize(i))).ptr; |
| 185 | } | 175 | } |
| 186 | } | 176 | } |
| 187 | } | 177 | } |
| 188 | | 178 | |
| 189 | /// Only shrinks capacity or retains current capacity | 179 | /// Only shrinks capacity or retains current capacity |
| 190 | pub fn shrinkCapacity(self: *Self, new_capacity: usize) void { | 180 | pub fn shrinkCapacity(self: *Self, allocator: Allocator, new_capacity: usize) void { |
| 191 | if (new_capacity <= prealloc_item_count) { | 181 | if (new_capacity <= prealloc_item_count) { |
| 192 | const len = @intCast(ShelfIndex, self.dynamic_segments.len); | 182 | const len = @intCast(ShelfIndex, self.dynamic_segments.len); |
| 193 | self.freeShelves(len, 0); | 183 | self.freeShelves(allocator, len, 0); |
| 194 | self.allocator.free(self.dynamic_segments); | 184 | allocator.free(self.dynamic_segments); |
| 195 | self.dynamic_segments = &[_][*]T{}; | 185 | self.dynamic_segments = &[_][*]T{}; |
| 196 | return; | 186 | return; |
| 197 | } | 187 | } |
| ... | @@ -203,8 +193,8 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type | ... | @@ -203,8 +193,8 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 203 | return; | 193 | return; |
| 204 | } | 194 | } |
| 205 | | 195 | |
| 206 | self.freeShelves(old_shelf_count, new_cap_shelf_count); | 196 | self.freeShelves(allocator, old_shelf_count, new_cap_shelf_count); |
| 207 | self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, new_cap_shelf_count); | 197 | self.dynamic_segments = allocator.shrink(self.dynamic_segments, new_cap_shelf_count); |
| 208 | } | 198 | } |
| 209 | | 199 | |
| 210 | pub fn shrink(self: *Self, new_len: usize) void { | 200 | pub fn shrink(self: *Self, new_len: usize) void { |
| ... | @@ -278,11 +268,11 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type | ... | @@ -278,11 +268,11 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 278 | return list_index + prealloc_item_count - (@as(usize, 1) << ((prealloc_exp + 1) + shelf_index)); | 268 | return list_index + prealloc_item_count - (@as(usize, 1) << ((prealloc_exp + 1) + shelf_index)); |
| 279 | } | 269 | } |
| 280 | | 270 | |
| 281 | fn freeShelves(self: *Self, from_count: ShelfIndex, to_count: ShelfIndex) void { | 271 | fn freeShelves(self: *Self, allocator: Allocator, from_count: ShelfIndex, to_count: ShelfIndex) void { |
| 282 | var i = from_count; | 272 | var i = from_count; |
| 283 | while (i != to_count) { | 273 | while (i != to_count) { |
| 284 | i -= 1; | 274 | i -= 1; |
| 285 | self.allocator.free(self.dynamic_segments[i][0..shelfSize(i)]); | 275 | allocator.free(self.dynamic_segments[i][0..shelfSize(i)]); |
| 286 | } | 276 | } |
| 287 | } | 277 | } |
| 288 | | 278 | |
| ... | @@ -367,24 +357,24 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type | ... | @@ -367,24 +357,24 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 367 | } | 357 | } |
| 368 | | 358 | |
| 369 | test "basic usage" { | 359 | test "basic usage" { |
| 370 | const a = std.testing.allocator; | 360 | try testSegmentedList(0); |
| 371 | | 361 | try testSegmentedList(1); |
| 372 | try testSegmentedList(0, a); | 362 | try testSegmentedList(2); |
| 373 | try testSegmentedList(1, a); | 363 | try testSegmentedList(4); |
| 374 | try testSegmentedList(2, a); | 364 | try testSegmentedList(8); |
| 375 | try testSegmentedList(4, a); | 365 | try testSegmentedList(16); |
| 376 | try testSegmentedList(8, a); | | |
| 377 | try testSegmentedList(16, a); | | |
| 378 | } | 366 | } |
| 379 | | 367 | |
| 380 | fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { | 368 | fn testSegmentedList(comptime prealloc: usize) !void { |
| 381 | var list = SegmentedList(i32, prealloc).init(allocator); | 369 | const gpa = std.testing.allocator; |
| 382 | defer list.deinit(); | 370 | |
| | 371 | var list: SegmentedList(i32, prealloc) = .{}; |
| | 372 | defer list.deinit(gpa); |
| 383 | | 373 | |
| 384 | { | 374 | { |
| 385 | var i: usize = 0; | 375 | var i: usize = 0; |
| 386 | while (i < 100) : (i += 1) { | 376 | while (i < 100) : (i += 1) { |
| 387 | try list.push(@intCast(i32, i + 1)); | 377 | try list.append(gpa, @intCast(i32, i + 1)); |
| 388 | try testing.expect(list.len == i + 1); | 378 | try testing.expect(list.len == i + 1); |
| 389 | } | 379 | } |
| 390 | } | 380 | } |
| ... | @@ -413,21 +403,21 @@ fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { | ... | @@ -413,21 +403,21 @@ fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { |
| 413 | try testing.expect(list.pop().? == 100); | 403 | try testing.expect(list.pop().? == 100); |
| 414 | try testing.expect(list.len == 99); | 404 | try testing.expect(list.len == 99); |
| 415 | | 405 | |
| 416 | try list.pushMany(&[_]i32{ 1, 2, 3 }); | 406 | try list.appendSlice(gpa, &[_]i32{ 1, 2, 3 }); |
| 417 | try testing.expect(list.len == 102); | 407 | try testing.expect(list.len == 102); |
| 418 | try testing.expect(list.pop().? == 3); | 408 | try testing.expect(list.pop().? == 3); |
| 419 | try testing.expect(list.pop().? == 2); | 409 | try testing.expect(list.pop().? == 2); |
| 420 | try testing.expect(list.pop().? == 1); | 410 | try testing.expect(list.pop().? == 1); |
| 421 | try testing.expect(list.len == 99); | 411 | try testing.expect(list.len == 99); |
| 422 | | 412 | |
| 423 | try list.pushMany(&[_]i32{}); | 413 | try list.appendSlice(gpa, &[_]i32{}); |
| 424 | try testing.expect(list.len == 99); | 414 | try testing.expect(list.len == 99); |
| 425 | | 415 | |
| 426 | { | 416 | { |
| 427 | var i: i32 = 99; | 417 | var i: i32 = 99; |
| 428 | while (list.pop()) |item| : (i -= 1) { | 418 | while (list.pop()) |item| : (i -= 1) { |
| 429 | try testing.expect(item == i); | 419 | try testing.expect(item == i); |
| 430 | list.shrinkCapacity(list.len); | 420 | list.shrinkCapacity(gpa, list.len); |
| 431 | } | 421 | } |
| 432 | } | 422 | } |
| 433 | | 423 | |
| ... | @@ -437,7 +427,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { | ... | @@ -437,7 +427,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { |
| 437 | | 427 | |
| 438 | var i: i32 = 0; | 428 | var i: i32 = 0; |
| 439 | while (i < 100) : (i += 1) { | 429 | while (i < 100) : (i += 1) { |
| 440 | try list.push(i + 1); | 430 | try list.append(gpa, i + 1); |
| 441 | control[@intCast(usize, i)] = i + 1; | 431 | control[@intCast(usize, i)] = i + 1; |
| 442 | } | 432 | } |
| 443 | | 433 | |
| ... | @@ -450,7 +440,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { | ... | @@ -450,7 +440,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { |
| 450 | try testing.expect(std.mem.eql(i32, control[50..], dest[50..])); | 440 | try testing.expect(std.mem.eql(i32, control[50..], dest[50..])); |
| 451 | } | 441 | } |
| 452 | | 442 | |
| 453 | try list.setCapacity(0); | 443 | try list.setCapacity(gpa, 0); |
| 454 | } | 444 | } |
| 455 | | 445 | |
| 456 | /// TODO look into why this std.math function was changed in | 446 | /// TODO look into why this std.math function was changed in |