authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-04-18 08:28:43-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-04-20 05:31:24-07:00
log99112b63bdeec512e164f289556b24fa1554a775
treea774fe70f74dec3f35430865eb3138ad3a317066
parent711bf55eaa643c3d05640bebbf3e4315477b8ed8

std.SegmentedList: breaking API changes

* Remove the Allocator field; instead it must be passed in as a parameter to any function that needs it. * Rename `push` to `append` and `pushMany` to `appendSlice` to match the conventions set by ArrayList.

1 files changed, 48 insertions(+), 58 deletions(-)

lib/std/segmented_list.zig+48-58
...@@ -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)
6262
63/// This is a stack data structure where pointers to indexes have the same lifetime as the data structure63/// 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 require68/// 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 use70/// 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 maximum75/// 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 };
9393
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,
9897
99 pub const prealloc_count = prealloc_item_count;98 pub const prealloc_count = prealloc_item_count;
10099
...@@ -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 }
108107
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 }
124113
...@@ -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 }
133122
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 }
138127
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 }
144133
...@@ -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 }
153142
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 }
161150
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 }
171161
172 /// Only grows capacity, or retains current capacity162 /// 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 }
188178
189 /// Only shrinks capacity or retains current capacity179 /// 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 }
205195
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 }
209199
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 }
280270
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 }
288278
...@@ -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}
368358
369test "basic usage" {359test "basic usage" {
370 const a = std.testing.allocator;360 try testSegmentedList(0);
371361 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}
379367
380fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void {368fn 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);
383373
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);
415405
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);
422412
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);
425415
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 }
433423
...@@ -437,7 +427,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void {...@@ -437,7 +427,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void {
437427
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 }
443433
...@@ -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 }
452442
453 try list.setCapacity(0);443 try list.setCapacity(gpa, 0);
454}444}
455445
456/// TODO look into why this std.math function was changed in446/// TODO look into why this std.math function was changed in