| author | |
| committer | |
| log | 78ba3b84850c1e1494797f76d147bc8c87f749e1 |
| tree | 6cecc99e50a6ea7518dc3100f9adddf137a69e9c |
| parent | 7fdbaeca728312d22d877a711b46e157d3b01fe7 |
| parent | 81007d0a4beed18314e948d3afbcdc7a8cfd73a9 |
| signature |
Segmented list implementation5 files changed, 334 insertions(+), 34 deletions(-)
CMakeLists.txt+29-28| ... | @@ -416,8 +416,8 @@ set(ZIG_CPP_SOURCES | ... | @@ -416,8 +416,8 @@ set(ZIG_CPP_SOURCES |
| 416 | set(ZIG_STD_FILES | 416 | set(ZIG_STD_FILES |
| 417 | "array_list.zig" | 417 | "array_list.zig" |
| 418 | "atomic/index.zig" | 418 | "atomic/index.zig" |
| 419 | "atomic/stack.zig" | ||
| 420 | "atomic/queue.zig" | 419 | "atomic/queue.zig" |
| 420 | "atomic/stack.zig" | ||
| 421 | "base64.zig" | 421 | "base64.zig" |
| 422 | "buf_map.zig" | 422 | "buf_map.zig" |
| 423 | "buf_set.zig" | 423 | "buf_set.zig" |
| ... | @@ -427,13 +427,13 @@ set(ZIG_STD_FILES | ... | @@ -427,13 +427,13 @@ set(ZIG_STD_FILES |
| 427 | "c/index.zig" | 427 | "c/index.zig" |
| 428 | "c/linux.zig" | 428 | "c/linux.zig" |
| 429 | "c/windows.zig" | 429 | "c/windows.zig" |
| 430 | "crypto/blake2.zig" | ||
| 431 | "crypto/hmac.zig" | ||
| 430 | "crypto/index.zig" | 432 | "crypto/index.zig" |
| 431 | "crypto/md5.zig" | 433 | "crypto/md5.zig" |
| 432 | "crypto/sha1.zig" | 434 | "crypto/sha1.zig" |
| 433 | "crypto/sha2.zig" | 435 | "crypto/sha2.zig" |
| 434 | "crypto/sha3.zig" | 436 | "crypto/sha3.zig" |
| 435 | "crypto/blake2.zig" | ||
| 436 | "crypto/hmac.zig" | ||
| 437 | "cstr.zig" | 437 | "cstr.zig" |
| 438 | "debug/failing_allocator.zig" | 438 | "debug/failing_allocator.zig" |
| 439 | "debug/index.zig" | 439 | "debug/index.zig" |
| ... | @@ -445,12 +445,12 @@ set(ZIG_STD_FILES | ... | @@ -445,12 +445,12 @@ set(ZIG_STD_FILES |
| 445 | "fmt/errol/index.zig" | 445 | "fmt/errol/index.zig" |
| 446 | "fmt/errol/lookup.zig" | 446 | "fmt/errol/lookup.zig" |
| 447 | "fmt/index.zig" | 447 | "fmt/index.zig" |
| 448 | "hash_map.zig" | ||
| 449 | "hash/index.zig" | ||
| 450 | "hash/adler.zig" | 448 | "hash/adler.zig" |
| 451 | "hash/crc.zig" | 449 | "hash/crc.zig" |
| 452 | "hash/fnv.zig" | 450 | "hash/fnv.zig" |
| 451 | "hash/index.zig" | ||
| 453 | "hash/siphash.zig" | 452 | "hash/siphash.zig" |
| 453 | "hash_map.zig" | ||
| 454 | "heap.zig" | 454 | "heap.zig" |
| 455 | "index.zig" | 455 | "index.zig" |
| 456 | "io.zig" | 456 | "io.zig" |
| ... | @@ -466,6 +466,28 @@ set(ZIG_STD_FILES | ... | @@ -466,6 +466,28 @@ set(ZIG_STD_FILES |
| 466 | "math/atanh.zig" | 466 | "math/atanh.zig" |
| 467 | "math/cbrt.zig" | 467 | "math/cbrt.zig" |
| 468 | "math/ceil.zig" | 468 | "math/ceil.zig" |
| 469 | "math/complex/abs.zig" | ||
| 470 | "math/complex/acos.zig" | ||
| 471 | "math/complex/acosh.zig" | ||
| 472 | "math/complex/arg.zig" | ||
| 473 | "math/complex/asin.zig" | ||
| 474 | "math/complex/asinh.zig" | ||
| 475 | "math/complex/atan.zig" | ||
| 476 | "math/complex/atanh.zig" | ||
| 477 | "math/complex/conj.zig" | ||
| 478 | "math/complex/cos.zig" | ||
| 479 | "math/complex/cosh.zig" | ||
| 480 | "math/complex/exp.zig" | ||
| 481 | "math/complex/index.zig" | ||
| 482 | "math/complex/ldexp.zig" | ||
| 483 | "math/complex/log.zig" | ||
| 484 | "math/complex/pow.zig" | ||
| 485 | "math/complex/proj.zig" | ||
| 486 | "math/complex/sin.zig" | ||
| 487 | "math/complex/sinh.zig" | ||
| 488 | "math/complex/sqrt.zig" | ||
| 489 | "math/complex/tan.zig" | ||
| 490 | "math/complex/tanh.zig" | ||
| 469 | "math/copysign.zig" | 491 | "math/copysign.zig" |
| 470 | "math/cos.zig" | 492 | "math/cos.zig" |
| 471 | "math/cosh.zig" | 493 | "math/cosh.zig" |
| ... | @@ -502,33 +524,12 @@ set(ZIG_STD_FILES | ... | @@ -502,33 +524,12 @@ set(ZIG_STD_FILES |
| 502 | "math/tan.zig" | 524 | "math/tan.zig" |
| 503 | "math/tanh.zig" | 525 | "math/tanh.zig" |
| 504 | "math/trunc.zig" | 526 | "math/trunc.zig" |
| 505 | "math/complex/abs.zig" | ||
| 506 | "math/complex/acosh.zig" | ||
| 507 | "math/complex/acos.zig" | ||
| 508 | "math/complex/arg.zig" | ||
| 509 | "math/complex/asinh.zig" | ||
| 510 | "math/complex/asin.zig" | ||
| 511 | "math/complex/atanh.zig" | ||
| 512 | "math/complex/atan.zig" | ||
| 513 | "math/complex/conj.zig" | ||
| 514 | "math/complex/cosh.zig" | ||
| 515 | "math/complex/cos.zig" | ||
| 516 | "math/complex/exp.zig" | ||
| 517 | "math/complex/index.zig" | ||
| 518 | "math/complex/ldexp.zig" | ||
| 519 | "math/complex/log.zig" | ||
| 520 | "math/complex/pow.zig" | ||
| 521 | "math/complex/proj.zig" | ||
| 522 | "math/complex/sinh.zig" | ||
| 523 | "math/complex/sin.zig" | ||
| 524 | "math/complex/sqrt.zig" | ||
| 525 | "math/complex/tanh.zig" | ||
| 526 | "math/complex/tan.zig" | ||
| 527 | "mem.zig" | 527 | "mem.zig" |
| 528 | "net.zig" | 528 | "net.zig" |
| 529 | "os/child_process.zig" | 529 | "os/child_process.zig" |
| 530 | "os/darwin.zig" | 530 | "os/darwin.zig" |
| 531 | "os/darwin_errno.zig" | 531 | "os/darwin_errno.zig" |
| 532 | "os/epoch.zig" | ||
| 532 | "os/file.zig" | 533 | "os/file.zig" |
| 533 | "os/get_user_id.zig" | 534 | "os/get_user_id.zig" |
| 534 | "os/index.zig" | 535 | "os/index.zig" |
| ... | @@ -538,13 +539,13 @@ set(ZIG_STD_FILES | ... | @@ -538,13 +539,13 @@ set(ZIG_STD_FILES |
| 538 | "os/linux/x86_64.zig" | 539 | "os/linux/x86_64.zig" |
| 539 | "os/path.zig" | 540 | "os/path.zig" |
| 540 | "os/time.zig" | 541 | "os/time.zig" |
| 541 | "os/epoch.zig" | ||
| 542 | "os/windows/error.zig" | 542 | "os/windows/error.zig" |
| 543 | "os/windows/index.zig" | 543 | "os/windows/index.zig" |
| 544 | "os/windows/util.zig" | 544 | "os/windows/util.zig" |
| 545 | "os/zen.zig" | 545 | "os/zen.zig" |
| 546 | "rand/index.zig" | 546 | "rand/index.zig" |
| 547 | "rand/ziggurat.zig" | 547 | "rand/ziggurat.zig" |
| 548 | "segmented_list.zig" | ||
| 548 | "sort.zig" | 549 | "sort.zig" |
| 549 | "special/bootstrap.zig" | 550 | "special/bootstrap.zig" |
| 550 | "special/bootstrap_lib.zig" | 551 | "special/bootstrap_lib.zig" |
std/index.zig+2| ... | @@ -7,6 +7,7 @@ pub const BufferOutStream = @import("buffer.zig").BufferOutStream; | ... | @@ -7,6 +7,7 @@ pub const BufferOutStream = @import("buffer.zig").BufferOutStream; |
| 7 | pub const HashMap = @import("hash_map.zig").HashMap; | 7 | pub const HashMap = @import("hash_map.zig").HashMap; |
| 8 | pub const LinkedList = @import("linked_list.zig").LinkedList; | 8 | pub const LinkedList = @import("linked_list.zig").LinkedList; |
| 9 | pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList; | 9 | pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList; |
| 10 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; | ||
| 10 | 11 | ||
| 11 | pub const atomic = @import("atomic/index.zig"); | 12 | pub const atomic = @import("atomic/index.zig"); |
| 12 | pub const base64 = @import("base64.zig"); | 13 | pub const base64 = @import("base64.zig"); |
| ... | @@ -43,6 +44,7 @@ test "std" { | ... | @@ -43,6 +44,7 @@ test "std" { |
| 43 | _ = @import("buffer.zig"); | 44 | _ = @import("buffer.zig"); |
| 44 | _ = @import("hash_map.zig"); | 45 | _ = @import("hash_map.zig"); |
| 45 | _ = @import("linked_list.zig"); | 46 | _ = @import("linked_list.zig"); |
| 47 | _ = @import("segmented_list.zig"); | ||
| 46 | 48 | ||
| 47 | _ = @import("base64.zig"); | 49 | _ = @import("base64.zig"); |
| 48 | _ = @import("build.zig"); | 50 | _ = @import("build.zig"); |
std/math/index.zig+26| ... | @@ -558,6 +558,32 @@ test "math.floorPowerOfTwo" { | ... | @@ -558,6 +558,32 @@ test "math.floorPowerOfTwo" { |
| 558 | comptime testFloorPowerOfTwo(); | 558 | comptime testFloorPowerOfTwo(); |
| 559 | } | 559 | } |
| 560 | 560 | ||
| 561 | pub fn log2_int(comptime T: type, x: T) Log2Int(T) { | ||
| 562 | assert(x != 0); | ||
| 563 | return Log2Int(T)(T.bit_count - 1 - @clz(x)); | ||
| 564 | } | ||
| 565 | |||
| 566 | pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) { | ||
| 567 | assert(x != 0); | ||
| 568 | const log2_val = log2_int(T, x); | ||
| 569 | if (T(1) << log2_val == x) | ||
| 570 | return log2_val; | ||
| 571 | return log2_val + 1; | ||
| 572 | } | ||
| 573 | |||
| 574 | test "std.math.log2_int_ceil" { | ||
| 575 | assert(log2_int_ceil(u32, 1) == 0); | ||
| 576 | assert(log2_int_ceil(u32, 2) == 1); | ||
| 577 | assert(log2_int_ceil(u32, 3) == 2); | ||
| 578 | assert(log2_int_ceil(u32, 4) == 2); | ||
| 579 | assert(log2_int_ceil(u32, 5) == 3); | ||
| 580 | assert(log2_int_ceil(u32, 6) == 3); | ||
| 581 | assert(log2_int_ceil(u32, 7) == 3); | ||
| 582 | assert(log2_int_ceil(u32, 8) == 3); | ||
| 583 | assert(log2_int_ceil(u32, 9) == 4); | ||
| 584 | assert(log2_int_ceil(u32, 10) == 4); | ||
| 585 | } | ||
| 586 | |||
| 561 | fn testFloorPowerOfTwo() void { | 587 | fn testFloorPowerOfTwo() void { |
| 562 | assert(floorPowerOfTwo(u32, 63) == 32); | 588 | assert(floorPowerOfTwo(u32, 63) == 32); |
| 563 | assert(floorPowerOfTwo(u32, 64) == 64); | 589 | assert(floorPowerOfTwo(u32, 64) == 64); |
std/math/log2.zig+1-6| ... | @@ -31,17 +31,12 @@ pub fn log2(x: var) @typeOf(x) { | ... | @@ -31,17 +31,12 @@ pub fn log2(x: var) @typeOf(x) { |
| 31 | return result; | 31 | return result; |
| 32 | }, | 32 | }, |
| 33 | TypeId.Int => { | 33 | TypeId.Int => { |
| 34 | return log2_int(T, x); | 34 | return math.log2_int(T, x); |
| 35 | }, | 35 | }, |
| 36 | else => @compileError("log2 not implemented for " ++ @typeName(T)), | 36 | else => @compileError("log2 not implemented for " ++ @typeName(T)), |
| 37 | } | 37 | } |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | pub fn log2_int(comptime T: type, x: T) T { | ||
| 41 | assert(x != 0); | ||
| 42 | return T.bit_count - 1 - T(@clz(x)); | ||
| 43 | } | ||
| 44 | |||
| 45 | pub fn log2_32(x_: f32) f32 { | 40 | pub fn log2_32(x_: f32) f32 { |
| 46 | const ivln2hi: f32 = 1.4428710938e+00; | 41 | const ivln2hi: f32 = 1.4428710938e+00; |
| 47 | const ivln2lo: f32 = -1.7605285393e-04; | 42 | const ivln2lo: f32 = -1.7605285393e-04; |
std/segmented_list.zig created+276| ... | @@ -0,0 +1,276 @@ | ||
| 1 | const std = @import("index.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const Allocator = std.mem.Allocator; | ||
| 4 | |||
| 5 | // Imagine that `fn at(self: &Self, index: usize) &T` is a customer asking for a box | ||
| 6 | // from a warehouse, based on a flat array, boxes ordered from 0 to N - 1. | ||
| 7 | // But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes. | ||
| 8 | // So when the customer requests a box index, we have to translate it to shelf index | ||
| 9 | // and box index within that shelf. Illustration: | ||
| 10 | // | ||
| 11 | // customer indexes: | ||
| 12 | // shelf 0: 0 | ||
| 13 | // shelf 1: 1 2 | ||
| 14 | // shelf 2: 3 4 5 6 | ||
| 15 | // shelf 3: 7 8 9 10 11 12 13 14 | ||
| 16 | // shelf 4: 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ||
| 17 | // 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 | ||
| 18 | // ... | ||
| 19 | // | ||
| 20 | // warehouse indexes: | ||
| 21 | // shelf 0: 0 | ||
| 22 | // shelf 1: 0 1 | ||
| 23 | // shelf 2: 0 1 2 3 | ||
| 24 | // shelf 3: 0 1 2 3 4 5 6 7 | ||
| 25 | // shelf 4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 26 | // 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 | ||
| 27 | // ... | ||
| 28 | // | ||
| 29 | // With this arrangement, here are the equations to get the shelf index and | ||
| 30 | // box index based on customer box index: | ||
| 31 | // | ||
| 32 | // shelf_index = floor(log2(customer_index + 1)) | ||
| 33 | // shelf_count = ceil(log2(box_count + 1)) | ||
| 34 | // box_index = customer_index + 1 - 2 ** shelf | ||
| 35 | // shelf_size = 2 ** shelf_index | ||
| 36 | // | ||
| 37 | // Now we complicate it a little bit further by adding a preallocated shelf, which must be | ||
| 38 | // a power of 2: | ||
| 39 | // prealloc=4 | ||
| 40 | // | ||
| 41 | // customer indexes: | ||
| 42 | // prealloc: 0 1 2 3 | ||
| 43 | // shelf 0: 4 5 6 7 8 9 10 11 | ||
| 44 | // shelf 1: 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ||
| 45 | // 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 | ||
| 46 | // ... | ||
| 47 | // | ||
| 48 | // warehouse indexes: | ||
| 49 | // prealloc: 0 1 2 3 | ||
| 50 | // shelf 0: 0 1 2 3 4 5 6 7 | ||
| 51 | // shelf 1: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 52 | // 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 | ||
| 53 | // ... | ||
| 54 | // | ||
| 55 | // Now the equations are: | ||
| 56 | // | ||
| 57 | // shelf_index = floor(log2(customer_index + prealloc)) - log2(prealloc) - 1 | ||
| 58 | // shelf_count = ceil(log2(box_count + prealloc)) - log2(prealloc) - 1 | ||
| 59 | // box_index = customer_index + prealloc - 2 ** (log2(prealloc) + 1 + shelf) | ||
| 60 | // shelf_size = prealloc * 2 ** (shelf_index + 1) | ||
| 61 | |||
| 62 | /// This is a stack data structure where pointers to indexes have the same lifetime as the data structure | ||
| 63 | /// itself, unlike ArrayList where push() invalidates all existing element pointers. | ||
| 64 | /// The tradeoff is that elements are not guaranteed to be contiguous. For that, use ArrayList. | ||
| 65 | /// Note however that most elements are contiguous, making this data structure cache-friendly. | ||
| 66 | /// | ||
| 67 | /// Because it never has to copy elements from an old location to a new location, it does not require | ||
| 68 | /// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator. | ||
| 69 | /// Note that the push() and pop() convenience methods perform a copy, but you can instead use | ||
| 70 | /// addOne(), at(), setCapacity(), and shrinkCapacity() to avoid copying items. | ||
| 71 | /// | ||
| 72 | /// This data structure has O(1) push and O(1) pop. | ||
| 73 | /// | ||
| 74 | /// It supports preallocated elements, making it especially well suited when the expected maximum | ||
| 75 | /// size is small. `prealloc_item_count` must be 0, or a power of 2. | ||
| 76 | pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type { | ||
| 77 | return struct { | ||
| 78 | const Self = this; | ||
| 79 | const prealloc_exp = blk: { | ||
| 80 | // we don't use the prealloc_exp constant when prealloc_item_count is 0. | ||
| 81 | assert(prealloc_item_count != 0); | ||
| 82 | |||
| 83 | const value = std.math.log2_int(usize, prealloc_item_count); | ||
| 84 | assert((1 << value) == prealloc_item_count); // prealloc_item_count must be a power of 2 | ||
| 85 | break :blk @typeOf(1)(value); | ||
| 86 | }; | ||
| 87 | const ShelfIndex = std.math.Log2Int(usize); | ||
| 88 | |||
| 89 | allocator: &Allocator, | ||
| 90 | len: usize, | ||
| 91 | prealloc_segment: [prealloc_item_count]T, | ||
| 92 | dynamic_segments: []&T, | ||
| 93 | |||
| 94 | /// Deinitialize with `deinit` | ||
| 95 | pub fn init(allocator: &Allocator) Self { | ||
| 96 | return Self { | ||
| 97 | .allocator = allocator, | ||
| 98 | .len = 0, | ||
| 99 | .prealloc_segment = undefined, | ||
| 100 | .dynamic_segments = []&T{}, | ||
| 101 | }; | ||
| 102 | } | ||
| 103 | |||
| 104 | pub fn deinit(self: &Self) void { | ||
| 105 | self.freeShelves(ShelfIndex(self.dynamic_segments.len), 0); | ||
| 106 | self.allocator.free(self.dynamic_segments); | ||
| 107 | *self = undefined; | ||
| 108 | } | ||
| 109 | |||
| 110 | pub fn at(self: &Self, i: usize) &T { | ||
| 111 | assert(i < self.len); | ||
| 112 | return self.uncheckedAt(i); | ||
| 113 | } | ||
| 114 | |||
| 115 | pub fn count(self: &const Self) usize { | ||
| 116 | return self.len; | ||
| 117 | } | ||
| 118 | |||
| 119 | pub fn push(self: &Self, item: &const T) !void { | ||
| 120 | const new_item_ptr = try self.addOne(); | ||
| 121 | *new_item_ptr = *item; | ||
| 122 | } | ||
| 123 | |||
| 124 | pub fn pushMany(self: &Self, items: []const T) !void { | ||
| 125 | for (items) |item| { | ||
| 126 | try self.push(item); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | pub fn pop(self: &Self) ?T { | ||
| 131 | if (self.len == 0) | ||
| 132 | return null; | ||
| 133 | |||
| 134 | const index = self.len - 1; | ||
| 135 | const result = *self.uncheckedAt(index); | ||
| 136 | self.len = index; | ||
| 137 | return result; | ||
| 138 | } | ||
| 139 | |||
| 140 | pub fn addOne(self: &Self) !&T { | ||
| 141 | const new_length = self.len + 1; | ||
| 142 | try self.setCapacity(new_length); | ||
| 143 | const result = self.uncheckedAt(self.len); | ||
| 144 | self.len = new_length; | ||
| 145 | return result; | ||
| 146 | } | ||
| 147 | |||
| 148 | pub fn setCapacity(self: &Self, new_capacity: usize) !void { | ||
| 149 | if (new_capacity <= prealloc_item_count) { | ||
| 150 | const len = ShelfIndex(self.dynamic_segments.len); | ||
| 151 | if (len == 0) return; | ||
| 152 | self.freeShelves(len, 0); | ||
| 153 | self.allocator.free(self.dynamic_segments); | ||
| 154 | self.dynamic_segments = []&T{}; | ||
| 155 | return; | ||
| 156 | } | ||
| 157 | |||
| 158 | const new_cap_shelf_count = shelfCount(new_capacity); | ||
| 159 | const old_shelf_count = ShelfIndex(self.dynamic_segments.len); | ||
| 160 | if (new_cap_shelf_count > old_shelf_count) { | ||
| 161 | self.dynamic_segments = try self.allocator.realloc(&T, self.dynamic_segments, new_cap_shelf_count); | ||
| 162 | var i = old_shelf_count; | ||
| 163 | errdefer { | ||
| 164 | self.freeShelves(i, old_shelf_count); | ||
| 165 | self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, old_shelf_count); | ||
| 166 | } | ||
| 167 | while (i < new_cap_shelf_count) : (i += 1) { | ||
| 168 | self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; | ||
| 169 | } | ||
| 170 | return; | ||
| 171 | } | ||
| 172 | if (new_cap_shelf_count == old_shelf_count) { | ||
| 173 | return; | ||
| 174 | } | ||
| 175 | self.freeShelves(old_shelf_count, new_cap_shelf_count); | ||
| 176 | self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, new_cap_shelf_count); | ||
| 177 | } | ||
| 178 | |||
| 179 | pub fn shrinkCapacity(self: &Self, new_capacity: usize) void { | ||
| 180 | assert(new_capacity <= prealloc_item_count or shelfCount(new_capacity) <= self.dynamic_segments.len); | ||
| 181 | self.setCapacity(new_capacity) catch unreachable; | ||
| 182 | } | ||
| 183 | |||
| 184 | pub fn uncheckedAt(self: &Self, index: usize) &T { | ||
| 185 | if (index < prealloc_item_count) { | ||
| 186 | return &self.prealloc_segment[index]; | ||
| 187 | } | ||
| 188 | const shelf_index = shelfIndex(index); | ||
| 189 | const box_index = boxIndex(index, shelf_index); | ||
| 190 | return &self.dynamic_segments[shelf_index][box_index]; | ||
| 191 | } | ||
| 192 | |||
| 193 | fn shelfCount(box_count: usize) ShelfIndex { | ||
| 194 | if (prealloc_item_count == 0) { | ||
| 195 | return std.math.log2_int_ceil(usize, box_count + 1); | ||
| 196 | } | ||
| 197 | return std.math.log2_int_ceil(usize, box_count + prealloc_item_count) - prealloc_exp - 1; | ||
| 198 | } | ||
| 199 | |||
| 200 | fn shelfSize(shelf_index: ShelfIndex) usize { | ||
| 201 | if (prealloc_item_count == 0) { | ||
| 202 | return usize(1) << shelf_index; | ||
| 203 | } | ||
| 204 | return usize(1) << (shelf_index + (prealloc_exp + 1)); | ||
| 205 | } | ||
| 206 | |||
| 207 | fn shelfIndex(list_index: usize) ShelfIndex { | ||
| 208 | if (prealloc_item_count == 0) { | ||
| 209 | return std.math.log2_int(usize, list_index + 1); | ||
| 210 | } | ||
| 211 | return std.math.log2_int(usize, list_index + prealloc_item_count) - prealloc_exp - 1; | ||
| 212 | } | ||
| 213 | |||
| 214 | fn boxIndex(list_index: usize, shelf_index: ShelfIndex) usize { | ||
| 215 | if (prealloc_item_count == 0) { | ||
| 216 | return (list_index + 1) - (usize(1) << shelf_index); | ||
| 217 | } | ||
| 218 | return list_index + prealloc_item_count - (usize(1) << ((prealloc_exp + 1) + shelf_index)); | ||
| 219 | } | ||
| 220 | |||
| 221 | fn freeShelves(self: &Self, from_count: ShelfIndex, to_count: ShelfIndex) void { | ||
| 222 | var i = from_count; | ||
| 223 | while (i != to_count) { | ||
| 224 | i -= 1; | ||
| 225 | self.allocator.free(self.dynamic_segments[i][0..shelfSize(i)]); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | }; | ||
| 230 | } | ||
| 231 | |||
| 232 | test "std.SegmentedList" { | ||
| 233 | var da = std.heap.DirectAllocator.init(); | ||
| 234 | defer da.deinit(); | ||
| 235 | var a = &da.allocator; | ||
| 236 | |||
| 237 | try testSegmentedList(0, a); | ||
| 238 | try testSegmentedList(1, a); | ||
| 239 | try testSegmentedList(2, a); | ||
| 240 | try testSegmentedList(4, a); | ||
| 241 | try testSegmentedList(8, a); | ||
| 242 | try testSegmentedList(16, a); | ||
| 243 | } | ||
| 244 | |||
| 245 | fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void { | ||
| 246 | var list = SegmentedList(i32, prealloc).init(allocator); | ||
| 247 | defer list.deinit(); | ||
| 248 | |||
| 249 | {var i: usize = 0; while (i < 100) : (i += 1) { | ||
| 250 | try list.push(i32(i + 1)); | ||
| 251 | assert(list.len == i + 1); | ||
| 252 | }} | ||
| 253 | |||
| 254 | {var i: usize = 0; while (i < 100) : (i += 1) { | ||
| 255 | assert(*list.at(i) == i32(i + 1)); | ||
| 256 | }} | ||
| 257 | |||
| 258 | assert(??list.pop() == 100); | ||
| 259 | assert(list.len == 99); | ||
| 260 | |||
| 261 | try list.pushMany([]i32 { 1, 2, 3 }); | ||
| 262 | assert(list.len == 102); | ||
| 263 | assert(??list.pop() == 3); | ||
| 264 | assert(??list.pop() == 2); | ||
| 265 | assert(??list.pop() == 1); | ||
| 266 | assert(list.len == 99); | ||
| 267 | |||
| 268 | try list.pushMany([]const i32 {}); | ||
| 269 | assert(list.len == 99); | ||
| 270 | |||
| 271 | var i: i32 = 99; | ||
| 272 | while (list.pop()) |item| : (i -= 1) { | ||
| 273 | assert(item == i); | ||
| 274 | list.shrinkCapacity(list.len); | ||
| 275 | } | ||
| 276 | } | ||