| ... | ... | @@ -61,16 +61,16 @@ const Allocator = std.mem.Allocator; |
| 61 | 61 | // shelf_size = prealloc * 2 ** (shelf_index + 1) |
| 62 | 62 | |
| 63 | 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 | 65 | /// The tradeoff is that elements are not guaranteed to be contiguous. For that, use ArrayList. |
| 66 | 66 | /// Note however that most elements are contiguous, making this data structure cache-friendly. |
| 67 | 67 | /// |
| 68 | 68 | /// Because it never has to copy elements from an old location to a new location, it does not require |
| 69 | 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 | 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 | 75 | /// It supports preallocated elements, making it especially well suited when the expected maximum |
| 76 | 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 | 91 | } |
| 92 | 92 | }; |
| 93 | 93 | |
| 94 | | prealloc_segment: [prealloc_item_count]T, |
| 95 | | dynamic_segments: [][*]T, |
| 96 | | allocator: Allocator, |
| 97 | | len: usize, |
| 94 | prealloc_segment: [prealloc_item_count]T = undefined, |
| 95 | dynamic_segments: [][*]T = &[_][*]T{}, |
| 96 | len: usize = 0, |
| 98 | 97 | |
| 99 | 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 | 105 | } |
| 107 | 106 | } |
| 108 | 107 | |
| 109 | | /// Deinitialize with `deinit` |
| 110 | | pub fn init(allocator: Allocator) Self { |
| 111 | | return Self{ |
| 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); |
| 108 | pub fn deinit(self: *Self, allocator: Allocator) void { |
| 109 | self.freeShelves(allocator, @intCast(ShelfIndex, self.dynamic_segments.len), 0); |
| 110 | allocator.free(self.dynamic_segments); |
| 122 | 111 | self.* = undefined; |
| 123 | 112 | } |
| 124 | 113 | |
| ... | ... | @@ -131,14 +120,14 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 131 | 120 | return self.len; |
| 132 | 121 | } |
| 133 | 122 | |
| 134 | | pub fn push(self: *Self, item: T) !void { |
| 135 | | const new_item_ptr = try self.addOne(); |
| 123 | pub fn append(self: *Self, allocator: Allocator, item: T) Allocator.Error!void { |
| 124 | const new_item_ptr = try self.addOne(allocator); |
| 136 | 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 | 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 | 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 | 144 | const new_length = self.len + 1; |
| 156 | | try self.growCapacity(new_length); |
| 145 | try self.growCapacity(allocator, new_length); |
| 157 | 146 | const result = uncheckedAt(self, self.len); |
| 158 | 147 | self.len = new_length; |
| 159 | 148 | return result; |
| 160 | 149 | } |
| 161 | 150 | |
| 162 | 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 | 154 | if (prealloc_item_count != 0) { |
| 165 | 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 | 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 | 164 | const new_cap_shelf_count = shelfCount(new_capacity); |
| 175 | 165 | const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); |
| 176 | 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 | 168 | var i = old_shelf_count; |
| 179 | 169 | errdefer { |
| 180 | | self.freeShelves(i, old_shelf_count); |
| 181 | | self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, old_shelf_count); |
| 170 | self.freeShelves(allocator, i, old_shelf_count); |
| 171 | self.dynamic_segments = allocator.shrink(self.dynamic_segments, old_shelf_count); |
| 182 | 172 | } |
| 183 | 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 | 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 | 181 | if (new_capacity <= prealloc_item_count) { |
| 192 | 182 | const len = @intCast(ShelfIndex, self.dynamic_segments.len); |
| 193 | | self.freeShelves(len, 0); |
| 194 | | self.allocator.free(self.dynamic_segments); |
| 183 | self.freeShelves(allocator, len, 0); |
| 184 | allocator.free(self.dynamic_segments); |
| 195 | 185 | self.dynamic_segments = &[_][*]T{}; |
| 196 | 186 | return; |
| 197 | 187 | } |
| ... | ... | @@ -203,8 +193,8 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 203 | 193 | return; |
| 204 | 194 | } |
| 205 | 195 | |
| 206 | | self.freeShelves(old_shelf_count, new_cap_shelf_count); |
| 207 | | self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, new_cap_shelf_count); |
| 196 | self.freeShelves(allocator, old_shelf_count, new_cap_shelf_count); |
| 197 | self.dynamic_segments = allocator.shrink(self.dynamic_segments, new_cap_shelf_count); |
| 208 | 198 | } |
| 209 | 199 | |
| 210 | 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 | 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 | 272 | var i = from_count; |
| 283 | 273 | while (i != to_count) { |
| 284 | 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 | 357 | } |
| 368 | 358 | |
| 369 | 359 | test "basic usage" { |
| 370 | | const a = std.testing.allocator; |
| 371 | | |
| 372 | | try testSegmentedList(0, a); |
| 373 | | try testSegmentedList(1, a); |
| 374 | | try testSegmentedList(2, a); |
| 375 | | try testSegmentedList(4, a); |
| 376 | | try testSegmentedList(8, a); |
| 377 | | try testSegmentedList(16, a); |
| 360 | try testSegmentedList(0); |
| 361 | try testSegmentedList(1); |
| 362 | try testSegmentedList(2); |
| 363 | try testSegmentedList(4); |
| 364 | try testSegmentedList(8); |
| 365 | try testSegmentedList(16); |
| 378 | 366 | } |
| 379 | 367 | |
| 380 | | fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { |
| 381 | | var list = SegmentedList(i32, prealloc).init(allocator); |
| 382 | | defer list.deinit(); |
| 368 | fn testSegmentedList(comptime prealloc: usize) !void { |
| 369 | const gpa = std.testing.allocator; |
| 370 | |
| 371 | var list: SegmentedList(i32, prealloc) = .{}; |
| 372 | defer list.deinit(gpa); |
| 383 | 373 | |
| 384 | 374 | { |
| 385 | 375 | var i: usize = 0; |
| 386 | 376 | while (i < 100) : (i += 1) { |
| 387 | | try list.push(@intCast(i32, i + 1)); |
| 377 | try list.append(gpa, @intCast(i32, i + 1)); |
| 388 | 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 | 403 | try testing.expect(list.pop().? == 100); |
| 414 | 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 | 407 | try testing.expect(list.len == 102); |
| 418 | 408 | try testing.expect(list.pop().? == 3); |
| 419 | 409 | try testing.expect(list.pop().? == 2); |
| 420 | 410 | try testing.expect(list.pop().? == 1); |
| 421 | 411 | try testing.expect(list.len == 99); |
| 422 | 412 | |
| 423 | | try list.pushMany(&[_]i32{}); |
| 413 | try list.appendSlice(gpa, &[_]i32{}); |
| 424 | 414 | try testing.expect(list.len == 99); |
| 425 | 415 | |
| 426 | 416 | { |
| 427 | 417 | var i: i32 = 99; |
| 428 | 418 | while (list.pop()) |item| : (i -= 1) { |
| 429 | 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 | 427 | |
| 438 | 428 | var i: i32 = 0; |
| 439 | 429 | while (i < 100) : (i += 1) { |
| 440 | | try list.push(i + 1); |
| 430 | try list.append(gpa, i + 1); |
| 441 | 431 | control[@intCast(usize, i)] = i + 1; |
| 442 | 432 | } |
| 443 | 433 | |
| ... | ... | @@ -450,7 +440,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { |
| 450 | 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 | 446 | /// TODO look into why this std.math function was changed in |