authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-11-30 13:03:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-11-30 13:04:26-07:00
log50a336fff899ebd8a687c453ec6beb18a5a9baf9
treefed3040adda23b08b73424dbb28e2b3448fa10d5
parentb587a42233728a523c89a24caab1c68d60774fac

move std.SegmentedList to the std-lib-orphanage

I spent a long time working on this data structure, and I still think it's a neat idea, but it has no business being in the std lib. I'm aware of the few remaining references to SegmentedList that exist in the std lib, but they are dead code, and so I'm leaving the dead references as a clue that the code is dead. Cleaning up dead code will be a separate effort that involves code coverage tools to make sure we find it all. std-lib-orphanage commit: 2c36a7894c689ecbaf63d5f489bb0c68773410c4 closes #7190

3 files changed, 16 insertions(+), 481 deletions(-)

lib/std/c/ast.zig+16-21
......@@ -4,29 +4,22 @@
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
66const std = @import("std");
7const SegmentedList = std.SegmentedList;
7const ArrayList = std.ArrayList;
88const Token = std.c.Token;
99const Source = std.c.tokenizer.Source;
1010
1111pub const TokenIndex = usize;
1212
1313pub const Tree = struct {
14 tokens: TokenList,
15 sources: SourceList,
14 tokens: []Token,
15 sources: []Source,
1616 root_node: *Node.Root,
17 arena_allocator: std.heap.ArenaAllocator,
18 msgs: MsgList,
19
20 pub const SourceList = SegmentedList(Source, 4);
21 pub const TokenList = Source.TokenList;
22 pub const MsgList = SegmentedList(Msg, 0);
17 arena_state: std.heap.ArenaAllocator.State,
18 gpa: *mem.Allocator,
19 msgs: []Msg,
2320
2421 pub fn deinit(self: *Tree) void {
25 // Here we copy the arena allocator into stack memory, because
26 // otherwise it would destroy itself while it was still working.
27 var arena_allocator = self.arena_allocator;
28 arena_allocator.deinit();
29 // self is destroyed
22 self.arena_state.promote(self.gpa).deinit();
3023 }
3124
3225 pub fn tokenSlice(tree: *Tree, token: TokenIndex) []const u8 {
......@@ -176,7 +169,8 @@ pub const Error = union(enum) {
176169};
177170
178171pub const Type = struct {
179 pub const TypeList = std.SegmentedList(*Type, 4);
172 pub const TypeList = ArrayList(*Type);
173
180174 @"const": bool = false,
181175 atomic: bool = false,
182176 @"volatile": bool = false,
......@@ -249,7 +243,7 @@ pub const Node = struct {
249243 decls: DeclList,
250244 eof: TokenIndex,
251245
252 pub const DeclList = SegmentedList(*Node, 4);
246 pub const DeclList = ArrayList(*Node);
253247 };
254248
255249 pub const DeclSpec = struct {
......@@ -568,8 +562,8 @@ pub const Node = struct {
568562 Array: Arrays,
569563 },
570564
571 pub const Arrays = std.SegmentedList(*Array, 2);
572 pub const Params = std.SegmentedList(*Param, 4);
565 pub const Arrays = ArrayList(*Array);
566 pub const Params = ArrayList(*Param);
573567 };
574568
575569 pub const Array = struct {
......@@ -612,7 +606,7 @@ pub const Node = struct {
612606 old_decls: OldDeclList,
613607 body: ?*CompoundStmt,
614608
615 pub const OldDeclList = SegmentedList(*Node, 0);
609 pub const OldDeclList = ArrayList(*Node);
616610 };
617611
618612 pub const Typedef = struct {
......@@ -642,11 +636,12 @@ pub const Node = struct {
642636
643637 pub const Initializer = union(enum) {
644638 list: struct {
645 initializers: InitializerList,
639 initializers: List,
646640 rbrace: TokenIndex,
647641 },
648642 expr: *Expr,
649 pub const InitializerList = std.SegmentedList(*Initializer, 4);
643
644 pub const List = ArrayList(*Initializer);
650645 };
651646
652647 pub const Macro = struct {
lib/std/segmented_list.zig deleted-459
......@@ -1,459 +0,0 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("std.zig");
7const assert = std.debug.assert;
8const testing = std.testing;
9const Allocator = std.mem.Allocator;
10
11// Imagine that `fn at(self: *Self, index: usize) &T` is a customer asking for a box
12// from a warehouse, based on a flat array, boxes ordered from 0 to N - 1.
13// But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes.
14// So when the customer requests a box index, we have to translate it to shelf index
15// and box index within that shelf. Illustration:
16//
17// customer indexes:
18// shelf 0: 0
19// shelf 1: 1 2
20// shelf 2: 3 4 5 6
21// shelf 3: 7 8 9 10 11 12 13 14
22// shelf 4: 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
23// shelf 5: 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
24// ...
25//
26// warehouse indexes:
27// shelf 0: 0
28// shelf 1: 0 1
29// shelf 2: 0 1 2 3
30// shelf 3: 0 1 2 3 4 5 6 7
31// shelf 4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
32// shelf 5: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
33// ...
34//
35// With this arrangement, here are the equations to get the shelf index and
36// box index based on customer box index:
37//
38// shelf_index = floor(log2(customer_index + 1))
39// shelf_count = ceil(log2(box_count + 1))
40// box_index = customer_index + 1 - 2 ** shelf
41// shelf_size = 2 ** shelf_index
42//
43// Now we complicate it a little bit further by adding a preallocated shelf, which must be
44// a power of 2:
45// prealloc=4
46//
47// customer indexes:
48// prealloc: 0 1 2 3
49// shelf 0: 4 5 6 7 8 9 10 11
50// shelf 1: 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
51// shelf 2: 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
52// ...
53//
54// warehouse indexes:
55// prealloc: 0 1 2 3
56// shelf 0: 0 1 2 3 4 5 6 7
57// shelf 1: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
58// shelf 2: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
59// ...
60//
61// Now the equations are:
62//
63// shelf_index = floor(log2(customer_index + prealloc)) - log2(prealloc) - 1
64// shelf_count = ceil(log2(box_count + prealloc)) - log2(prealloc) - 1
65// box_index = customer_index + prealloc - 2 ** (log2(prealloc) + 1 + shelf)
66// shelf_size = prealloc * 2 ** (shelf_index + 1)
67
68/// This is a stack data structure where pointers to indexes have the same lifetime as the data structure
69/// itself, unlike ArrayList where push() invalidates all existing element pointers.
70/// The tradeoff is that elements are not guaranteed to be contiguous. For that, use ArrayList.
71/// Note however that most elements are contiguous, making this data structure cache-friendly.
72///
73/// Because it never has to copy elements from an old location to a new location, it does not require
74/// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator.
75/// Note that the push() and pop() convenience methods perform a copy, but you can instead use
76/// addOne(), at(), setCapacity(), and shrinkCapacity() to avoid copying items.
77///
78/// This data structure has O(1) push and O(1) pop.
79///
80/// It supports preallocated elements, making it especially well suited when the expected maximum
81/// size is small. `prealloc_item_count` must be 0, or a power of 2.
82pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type {
83 return struct {
84 const Self = @This();
85 const ShelfIndex = std.math.Log2Int(usize);
86
87 const prealloc_exp: ShelfIndex = blk: {
88 // we don't use the prealloc_exp constant when prealloc_item_count is 0
89 // but lazy-init may still be triggered by other code so supply a value
90 if (prealloc_item_count == 0) {
91 break :blk 0;
92 } else {
93 assert(std.math.isPowerOfTwo(prealloc_item_count));
94 const value = std.math.log2_int(usize, prealloc_item_count);
95 break :blk value;
96 }
97 };
98
99 prealloc_segment: [prealloc_item_count]T,
100 dynamic_segments: [][*]T,
101 allocator: *Allocator,
102 len: usize,
103
104 pub const prealloc_count = prealloc_item_count;
105
106 fn AtType(comptime SelfType: type) type {
107 if (@typeInfo(SelfType).Pointer.is_const) {
108 return *const T;
109 } else {
110 return *T;
111 }
112 }
113
114 /// Deinitialize with `deinit`
115 pub fn init(allocator: *Allocator) Self {
116 return Self{
117 .allocator = allocator,
118 .len = 0,
119 .prealloc_segment = undefined,
120 .dynamic_segments = &[_][*]T{},
121 };
122 }
123
124 pub fn deinit(self: *Self) void {
125 self.freeShelves(@intCast(ShelfIndex, self.dynamic_segments.len), 0);
126 self.allocator.free(self.dynamic_segments);
127 self.* = undefined;
128 }
129
130 pub fn at(self: anytype, i: usize) AtType(@TypeOf(self)) {
131 assert(i < self.len);
132 return self.uncheckedAt(i);
133 }
134
135 pub fn count(self: Self) usize {
136 return self.len;
137 }
138
139 pub fn push(self: *Self, item: T) !void {
140 const new_item_ptr = try self.addOne();
141 new_item_ptr.* = item;
142 }
143
144 pub fn pushMany(self: *Self, items: []const T) !void {
145 for (items) |item| {
146 try self.push(item);
147 }
148 }
149
150 pub fn pop(self: *Self) ?T {
151 if (self.len == 0) return null;
152
153 const index = self.len - 1;
154 const result = uncheckedAt(self, index).*;
155 self.len = index;
156 return result;
157 }
158
159 pub fn addOne(self: *Self) !*T {
160 const new_length = self.len + 1;
161 try self.growCapacity(new_length);
162 const result = uncheckedAt(self, self.len);
163 self.len = new_length;
164 return result;
165 }
166
167 /// Grows or shrinks capacity to match usage.
168 pub fn setCapacity(self: *Self, new_capacity: usize) !void {
169 if (prealloc_item_count != 0) {
170 if (new_capacity <= @as(usize, 1) << (prealloc_exp + @intCast(ShelfIndex, self.dynamic_segments.len))) {
171 return self.shrinkCapacity(new_capacity);
172 }
173 }
174 return self.growCapacity(new_capacity);
175 }
176
177 /// Only grows capacity, or retains current capacity
178 pub fn growCapacity(self: *Self, new_capacity: usize) !void {
179 const new_cap_shelf_count = shelfCount(new_capacity);
180 const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len);
181 if (new_cap_shelf_count > old_shelf_count) {
182 self.dynamic_segments = try self.allocator.realloc(self.dynamic_segments, new_cap_shelf_count);
183 var i = old_shelf_count;
184 errdefer {
185 self.freeShelves(i, old_shelf_count);
186 self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, old_shelf_count);
187 }
188 while (i < new_cap_shelf_count) : (i += 1) {
189 self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr;
190 }
191 }
192 }
193
194 /// Only shrinks capacity or retains current capacity
195 pub fn shrinkCapacity(self: *Self, new_capacity: usize) void {
196 if (new_capacity <= prealloc_item_count) {
197 const len = @intCast(ShelfIndex, self.dynamic_segments.len);
198 self.freeShelves(len, 0);
199 self.allocator.free(self.dynamic_segments);
200 self.dynamic_segments = &[_][*]T{};
201 return;
202 }
203
204 const new_cap_shelf_count = shelfCount(new_capacity);
205 const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len);
206 assert(new_cap_shelf_count <= old_shelf_count);
207 if (new_cap_shelf_count == old_shelf_count) {
208 return;
209 }
210
211 self.freeShelves(old_shelf_count, new_cap_shelf_count);
212 self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, new_cap_shelf_count);
213 }
214
215 pub fn shrink(self: *Self, new_len: usize) void {
216 assert(new_len <= self.len);
217 // TODO take advantage of the new realloc semantics
218 self.len = new_len;
219 }
220
221 pub fn writeToSlice(self: *Self, dest: []T, start: usize) void {
222 const end = start + dest.len;
223 assert(end <= self.len);
224
225 var i = start;
226 if (end <= prealloc_item_count) {
227 std.mem.copy(T, dest[i - start ..], self.prealloc_segment[i..end]);
228 return;
229 } else if (i < prealloc_item_count) {
230 std.mem.copy(T, dest[i - start ..], self.prealloc_segment[i..]);
231 i = prealloc_item_count;
232 }
233
234 while (i < end) {
235 const shelf_index = shelfIndex(i);
236 const copy_start = boxIndex(i, shelf_index);
237 const copy_end = std.math.min(shelfSize(shelf_index), copy_start + end - i);
238
239 std.mem.copy(
240 T,
241 dest[i - start ..],
242 self.dynamic_segments[shelf_index][copy_start..copy_end],
243 );
244
245 i += (copy_end - copy_start);
246 }
247 }
248
249 pub fn uncheckedAt(self: anytype, index: usize) AtType(@TypeOf(self)) {
250 if (index < prealloc_item_count) {
251 return &self.prealloc_segment[index];
252 }
253 const shelf_index = shelfIndex(index);
254 const box_index = boxIndex(index, shelf_index);
255 return &self.dynamic_segments[shelf_index][box_index];
256 }
257
258 fn shelfCount(box_count: usize) ShelfIndex {
259 if (prealloc_item_count == 0) {
260 return std.math.log2_int_ceil(usize, box_count + 1);
261 }
262 return std.math.log2_int_ceil(usize, box_count + prealloc_item_count) - prealloc_exp - 1;
263 }
264
265 fn shelfSize(shelf_index: ShelfIndex) usize {
266 if (prealloc_item_count == 0) {
267 return @as(usize, 1) << shelf_index;
268 }
269 return @as(usize, 1) << (shelf_index + (prealloc_exp + 1));
270 }
271
272 fn shelfIndex(list_index: usize) ShelfIndex {
273 if (prealloc_item_count == 0) {
274 return std.math.log2_int(usize, list_index + 1);
275 }
276 return std.math.log2_int(usize, list_index + prealloc_item_count) - prealloc_exp - 1;
277 }
278
279 fn boxIndex(list_index: usize, shelf_index: ShelfIndex) usize {
280 if (prealloc_item_count == 0) {
281 return (list_index + 1) - (@as(usize, 1) << shelf_index);
282 }
283 return list_index + prealloc_item_count - (@as(usize, 1) << ((prealloc_exp + 1) + shelf_index));
284 }
285
286 fn freeShelves(self: *Self, from_count: ShelfIndex, to_count: ShelfIndex) void {
287 var i = from_count;
288 while (i != to_count) {
289 i -= 1;
290 self.allocator.free(self.dynamic_segments[i][0..shelfSize(i)]);
291 }
292 }
293
294 pub const Iterator = struct {
295 list: *Self,
296 index: usize,
297 box_index: usize,
298 shelf_index: ShelfIndex,
299 shelf_size: usize,
300
301 pub fn next(it: *Iterator) ?*T {
302 if (it.index >= it.list.len) return null;
303 if (it.index < prealloc_item_count) {
304 const ptr = &it.list.prealloc_segment[it.index];
305 it.index += 1;
306 if (it.index == prealloc_item_count) {
307 it.box_index = 0;
308 it.shelf_index = 0;
309 it.shelf_size = prealloc_item_count * 2;
310 }
311 return ptr;
312 }
313
314 const ptr = &it.list.dynamic_segments[it.shelf_index][it.box_index];
315 it.index += 1;
316 it.box_index += 1;
317 if (it.box_index == it.shelf_size) {
318 it.shelf_index += 1;
319 it.box_index = 0;
320 it.shelf_size *= 2;
321 }
322 return ptr;
323 }
324
325 pub fn prev(it: *Iterator) ?*T {
326 if (it.index == 0) return null;
327
328 it.index -= 1;
329 if (it.index < prealloc_item_count) return &it.list.prealloc_segment[it.index];
330
331 if (it.box_index == 0) {
332 it.shelf_index -= 1;
333 it.shelf_size /= 2;
334 it.box_index = it.shelf_size - 1;
335 } else {
336 it.box_index -= 1;
337 }
338
339 return &it.list.dynamic_segments[it.shelf_index][it.box_index];
340 }
341
342 pub fn peek(it: *Iterator) ?*T {
343 if (it.index >= it.list.len)
344 return null;
345 if (it.index < prealloc_item_count)
346 return &it.list.prealloc_segment[it.index];
347
348 return &it.list.dynamic_segments[it.shelf_index][it.box_index];
349 }
350
351 pub fn set(it: *Iterator, index: usize) void {
352 it.index = index;
353 if (index < prealloc_item_count) return;
354 it.shelf_index = shelfIndex(index);
355 it.box_index = boxIndex(index, it.shelf_index);
356 it.shelf_size = shelfSize(it.shelf_index);
357 }
358 };
359
360 pub fn iterator(self: *Self, start_index: usize) Iterator {
361 var it = Iterator{
362 .list = self,
363 .index = undefined,
364 .shelf_index = undefined,
365 .box_index = undefined,
366 .shelf_size = undefined,
367 };
368 it.set(start_index);
369 return it;
370 }
371 };
372}
373
374test "std.SegmentedList" {
375 var a = std.testing.allocator;
376
377 try testSegmentedList(0, a);
378 try testSegmentedList(1, a);
379 try testSegmentedList(2, a);
380 try testSegmentedList(4, a);
381 try testSegmentedList(8, a);
382 try testSegmentedList(16, a);
383}
384
385fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void {
386 var list = SegmentedList(i32, prealloc).init(allocator);
387 defer list.deinit();
388
389 {
390 var i: usize = 0;
391 while (i < 100) : (i += 1) {
392 try list.push(@intCast(i32, i + 1));
393 testing.expect(list.len == i + 1);
394 }
395 }
396
397 {
398 var i: usize = 0;
399 while (i < 100) : (i += 1) {
400 testing.expect(list.at(i).* == @intCast(i32, i + 1));
401 }
402 }
403
404 {
405 var it = list.iterator(0);
406 var x: i32 = 0;
407 while (it.next()) |item| {
408 x += 1;
409 testing.expect(item.* == x);
410 }
411 testing.expect(x == 100);
412 while (it.prev()) |item| : (x -= 1) {
413 testing.expect(item.* == x);
414 }
415 testing.expect(x == 0);
416 }
417
418 testing.expect(list.pop().? == 100);
419 testing.expect(list.len == 99);
420
421 try list.pushMany(&[_]i32{ 1, 2, 3 });
422 testing.expect(list.len == 102);
423 testing.expect(list.pop().? == 3);
424 testing.expect(list.pop().? == 2);
425 testing.expect(list.pop().? == 1);
426 testing.expect(list.len == 99);
427
428 try list.pushMany(&[_]i32{});
429 testing.expect(list.len == 99);
430
431 {
432 var i: i32 = 99;
433 while (list.pop()) |item| : (i -= 1) {
434 testing.expect(item == i);
435 list.shrinkCapacity(list.len);
436 }
437 }
438
439 {
440 var control: [100]i32 = undefined;
441 var dest: [100]i32 = undefined;
442
443 var i: i32 = 0;
444 while (i < 100) : (i += 1) {
445 try list.push(i + 1);
446 control[@intCast(usize, i)] = i + 1;
447 }
448
449 std.mem.set(i32, dest[0..], 0);
450 list.writeToSlice(dest[0..], 0);
451 testing.expect(std.mem.eql(i32, control[0..], dest[0..]));
452
453 std.mem.set(i32, dest[0..], 0);
454 list.writeToSlice(dest[50..], 50);
455 testing.expect(std.mem.eql(i32, control[50..], dest[50..]));
456 }
457
458 try list.setCapacity(0);
459}
lib/std/std.zig-1
......@@ -31,7 +31,6 @@ pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceE
3131pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
3232pub const Progress = @import("progress.zig").Progress;
3333pub const ResetEvent = @import("reset_event.zig").ResetEvent;
34pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
3534pub const SemanticVersion = @import("SemanticVersion.zig");
3635pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
3736pub const SpinLock = @import("spinlock.zig").SpinLock;