tigerbeetle-node 0.11.13 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/aarch64-linux-gnu/client.node +0 -0
- package/dist/bin/aarch64-linux-musl/client.node +0 -0
- package/dist/bin/aarch64-macos/client.node +0 -0
- package/dist/bin/x86_64-linux-gnu/client.node +0 -0
- package/dist/bin/x86_64-linux-musl/client.node +0 -0
- package/dist/bin/x86_64-macos/client.node +0 -0
- package/dist/index.js +33 -1
- package/dist/index.js.map +1 -1
- package/package-lock.json +66 -0
- package/package.json +6 -16
- package/src/index.ts +56 -1
- package/src/node.zig +9 -9
- package/dist/.client.node.sha256 +0 -1
- package/scripts/build_lib.sh +0 -61
- package/scripts/download_node_headers.sh +0 -32
- package/src/tigerbeetle/scripts/benchmark.bat +0 -55
- package/src/tigerbeetle/scripts/benchmark.sh +0 -66
- package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
- package/src/tigerbeetle/scripts/fail_on_diff.sh +0 -9
- package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
- package/src/tigerbeetle/scripts/fuzz_loop_hash_log.sh +0 -12
- package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
- package/src/tigerbeetle/scripts/install.bat +0 -7
- package/src/tigerbeetle/scripts/install.sh +0 -21
- package/src/tigerbeetle/scripts/install_zig.bat +0 -113
- package/src/tigerbeetle/scripts/install_zig.sh +0 -90
- package/src/tigerbeetle/scripts/lint.zig +0 -199
- package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -55
- package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
- package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
- package/src/tigerbeetle/scripts/scripts/fail_on_diff.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
- package/src/tigerbeetle/scripts/scripts/fuzz_loop_hash_log.sh +0 -12
- package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
- package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
- package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
- package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
- package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
- package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
- package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
- package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
- package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
- package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
- package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
- package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
- package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
- package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
- package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
- package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
- package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
- package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
- package/src/tigerbeetle/src/benchmark.zig +0 -336
- package/src/tigerbeetle/src/config.zig +0 -233
- package/src/tigerbeetle/src/constants.zig +0 -428
- package/src/tigerbeetle/src/ewah.zig +0 -286
- package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
- package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
- package/src/tigerbeetle/src/fifo.zig +0 -120
- package/src/tigerbeetle/src/io/benchmark.zig +0 -213
- package/src/tigerbeetle/src/io/darwin.zig +0 -814
- package/src/tigerbeetle/src/io/linux.zig +0 -1071
- package/src/tigerbeetle/src/io/test.zig +0 -643
- package/src/tigerbeetle/src/io/windows.zig +0 -1183
- package/src/tigerbeetle/src/io.zig +0 -34
- package/src/tigerbeetle/src/iops.zig +0 -107
- package/src/tigerbeetle/src/lsm/README.md +0 -308
- package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
- package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
- package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
- package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
- package/src/tigerbeetle/src/lsm/direction.zig +0 -11
- package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
- package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
- package/src/tigerbeetle/src/lsm/forest.zig +0 -205
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -450
- package/src/tigerbeetle/src/lsm/grid.zig +0 -573
- package/src/tigerbeetle/src/lsm/groove.zig +0 -1036
- package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
- package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
- package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
- package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -878
- package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
- package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
- package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
- package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
- package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -381
- package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1329
- package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
- package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
- package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
- package/src/tigerbeetle/src/lsm/table.zig +0 -1009
- package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -192
- package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
- package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -203
- package/src/tigerbeetle/src/lsm/test.zig +0 -439
- package/src/tigerbeetle/src/lsm/tree.zig +0 -1169
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -479
- package/src/tigerbeetle/src/message_bus.zig +0 -1013
- package/src/tigerbeetle/src/message_pool.zig +0 -156
- package/src/tigerbeetle/src/ring_buffer.zig +0 -399
- package/src/tigerbeetle/src/simulator.zig +0 -580
- package/src/tigerbeetle/src/state_machine/auditor.zig +0 -578
- package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
- package/src/tigerbeetle/src/state_machine.zig +0 -2099
- package/src/tigerbeetle/src/static_allocator.zig +0 -65
- package/src/tigerbeetle/src/stdx.zig +0 -171
- package/src/tigerbeetle/src/storage.zig +0 -393
- package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
- package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
- package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
- package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
- package/src/tigerbeetle/src/testing/cluster.zig +0 -444
- package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
- package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
- package/src/tigerbeetle/src/testing/id.zig +0 -99
- package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -374
- package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
- package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
- package/src/tigerbeetle/src/testing/state_machine.zig +0 -250
- package/src/tigerbeetle/src/testing/storage.zig +0 -757
- package/src/tigerbeetle/src/testing/table.zig +0 -247
- package/src/tigerbeetle/src/testing/time.zig +0 -84
- package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
- package/src/tigerbeetle/src/time.zig +0 -112
- package/src/tigerbeetle/src/tracer.zig +0 -529
- package/src/tigerbeetle/src/unit_tests.zig +0 -40
- package/src/tigerbeetle/src/vopr.zig +0 -495
- package/src/tigerbeetle/src/vsr/README.md +0 -209
- package/src/tigerbeetle/src/vsr/client.zig +0 -544
- package/src/tigerbeetle/src/vsr/clock.zig +0 -855
- package/src/tigerbeetle/src/vsr/journal.zig +0 -2415
- package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
- package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
- package/src/tigerbeetle/src/vsr/replica.zig +0 -6616
- package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
- package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
- package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
- package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
- package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
- package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
- package/src/tigerbeetle/src/vsr.zig +0 -1425
|
@@ -1,1329 +0,0 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
|
-
const assert = std.debug.assert;
|
|
4
|
-
const math = std.math;
|
|
5
|
-
const mem = std.mem;
|
|
6
|
-
|
|
7
|
-
const stdx = @import("../stdx.zig");
|
|
8
|
-
const div_ceil = @import("../stdx.zig").div_ceil;
|
|
9
|
-
const binary_search_values_raw = @import("binary_search.zig").binary_search_values_raw;
|
|
10
|
-
const binary_search_keys = @import("binary_search.zig").binary_search_keys;
|
|
11
|
-
const Direction = @import("direction.zig").Direction;
|
|
12
|
-
|
|
13
|
-
/// A "segmented array" is an array with efficient (amortized) random-insert/remove operations.
|
|
14
|
-
/// Also known as an "unrolled linked list": https://en.wikipedia.org/wiki/Unrolled_linked_list
|
|
15
|
-
///
|
|
16
|
-
/// The structure consists of an array list of "nodes". Each node is a non-empty array of T.
|
|
17
|
-
/// When a node fills, it is split into two adjacent, partially-full nodes.
|
|
18
|
-
/// When a node empties, it is joined with a nearby node.
|
|
19
|
-
///
|
|
20
|
-
/// An absolute index is offset from the start of the segmented array.
|
|
21
|
-
/// A relative index is offset from the start of a node.
|
|
22
|
-
pub fn SegmentedArray(
|
|
23
|
-
comptime T: type,
|
|
24
|
-
comptime NodePool: type,
|
|
25
|
-
comptime element_count_max: u32,
|
|
26
|
-
comptime options: Options,
|
|
27
|
-
) type {
|
|
28
|
-
return SegmentedArrayType(T, NodePool, element_count_max, null, {}, {}, options);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
pub fn SortedSegmentedArray(
|
|
32
|
-
comptime T: type,
|
|
33
|
-
comptime NodePool: type,
|
|
34
|
-
comptime element_count_max: u32,
|
|
35
|
-
comptime Key: type,
|
|
36
|
-
comptime key_from_value: fn (*const T) callconv(.Inline) Key,
|
|
37
|
-
comptime compare_keys: fn (Key, Key) callconv(.Inline) math.Order,
|
|
38
|
-
comptime options: Options,
|
|
39
|
-
) type {
|
|
40
|
-
return SegmentedArrayType(T, NodePool, element_count_max, Key, key_from_value, compare_keys, options);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
pub const Options = struct {
|
|
44
|
-
/// Assert all invariants before/after every public function.
|
|
45
|
-
/// Very expensive - only enable for debugging/fuzzing.
|
|
46
|
-
verify: bool = false,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
fn SegmentedArrayType(
|
|
50
|
-
comptime T: type,
|
|
51
|
-
comptime NodePool: type,
|
|
52
|
-
comptime element_count_max: u32,
|
|
53
|
-
// Set when the SegmentedArray is ordered:
|
|
54
|
-
comptime Key: ?type,
|
|
55
|
-
comptime key_from_value: if (Key) |K| (fn (*const T) callconv(.Inline) K) else void,
|
|
56
|
-
comptime compare_keys: if (Key) |K| (fn (K, K) callconv(.Inline) math.Order) else void,
|
|
57
|
-
comptime options: Options,
|
|
58
|
-
) type {
|
|
59
|
-
return struct {
|
|
60
|
-
const Self = @This();
|
|
61
|
-
|
|
62
|
-
pub const Cursor = struct {
|
|
63
|
-
node: u32,
|
|
64
|
-
relative_index: u32,
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// We can't use @divExact() here as we store TableInfo structs of various sizes in this
|
|
68
|
-
// data structure. This means that there may be padding at the end of the node.
|
|
69
|
-
pub const node_capacity = blk: {
|
|
70
|
-
const max = @divFloor(NodePool.node_size, @sizeOf(T));
|
|
71
|
-
|
|
72
|
-
// We require that the node capacity is evenly divisible by 2 to simplify our code
|
|
73
|
-
// that splits/joins nodes at the midpoint.
|
|
74
|
-
const capacity = if (max % 2 == 0) max else max - 1;
|
|
75
|
-
|
|
76
|
-
assert(capacity >= 2);
|
|
77
|
-
assert(capacity % 2 == 0);
|
|
78
|
-
break :blk capacity;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
comptime {
|
|
82
|
-
// If this assert fails, we should be using a non-segmented array instead!
|
|
83
|
-
assert(element_count_max > node_capacity);
|
|
84
|
-
|
|
85
|
-
// We use u32 for indexes and counts.
|
|
86
|
-
assert(element_count_max <= std.math.maxInt(u32));
|
|
87
|
-
|
|
88
|
-
// The buffers returned from the node_pool must be able to store T with correct alignment.
|
|
89
|
-
assert(NodePool.node_alignment >= @alignOf(T));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
pub const node_count_max_naive = blk: {
|
|
93
|
-
// If a node fills up it is divided into two new nodes. Therefore,
|
|
94
|
-
// the worst possible space overhead is when all nodes are half full.
|
|
95
|
-
// This uses flooring division, we want to examine the worst case here.
|
|
96
|
-
const elements_per_node_min = @divExact(node_capacity, 2);
|
|
97
|
-
break :blk div_ceil(element_count_max, elements_per_node_min);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
// We can't always actually reach node_count_max_naive in all configurations.
|
|
101
|
-
// If we're at node_count_max_naive-1 nodes, in order to split one more node we need:
|
|
102
|
-
pub const node_count_max = if (element_count_max >=
|
|
103
|
-
// * The node that we split must be full.
|
|
104
|
-
node_capacity +
|
|
105
|
-
// * The last node must have at least one element.
|
|
106
|
-
1 +
|
|
107
|
-
// * All other nodes must be at least half-full.
|
|
108
|
-
((node_count_max_naive -| 3) * @divExact(node_capacity, 2)) +
|
|
109
|
-
// * And then we insert one more element into the full node.
|
|
110
|
-
1)
|
|
111
|
-
node_count_max_naive
|
|
112
|
-
else
|
|
113
|
-
node_count_max_naive - 1;
|
|
114
|
-
|
|
115
|
-
node_count: u32 = 0,
|
|
116
|
-
/// This is the segmented array. The first node_count pointers are non-null.
|
|
117
|
-
/// The rest are null. We only use optional pointers here to get safety checks.
|
|
118
|
-
nodes: *[node_count_max]?*[node_capacity]T,
|
|
119
|
-
/// Since nodes in a segmented array are usually not full, computing the absolute index
|
|
120
|
-
/// of an element in the full array is O(N) over the number of nodes. To avoid this cost
|
|
121
|
-
/// we precompute the absolute index of the first element of each node.
|
|
122
|
-
/// To avoid a separate counts field, we derive the number of elements in a node from the
|
|
123
|
-
/// index of that node and the next node.
|
|
124
|
-
/// To avoid special casing the count() function for the last node, we increase the array
|
|
125
|
-
/// length by 1 and store the total element count in the last slot.
|
|
126
|
-
indexes: *[node_count_max + 1]u32,
|
|
127
|
-
|
|
128
|
-
pub fn init(allocator: mem.Allocator) !Self {
|
|
129
|
-
const nodes = try allocator.create([node_count_max]?*[node_capacity]T);
|
|
130
|
-
errdefer allocator.destroy(nodes);
|
|
131
|
-
|
|
132
|
-
const indexes = try allocator.create([node_count_max + 1]u32);
|
|
133
|
-
errdefer allocator.destroy(indexes);
|
|
134
|
-
|
|
135
|
-
mem.set(?*[node_capacity]T, nodes, null);
|
|
136
|
-
indexes[0] = 0;
|
|
137
|
-
|
|
138
|
-
const array = Self{
|
|
139
|
-
.nodes = nodes,
|
|
140
|
-
.indexes = indexes,
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
if (options.verify) array.verify();
|
|
144
|
-
|
|
145
|
-
return array;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
pub fn deinit(array: Self, allocator: mem.Allocator, node_pool: ?*NodePool) void {
|
|
149
|
-
if (options.verify) array.verify();
|
|
150
|
-
|
|
151
|
-
for (array.nodes[0..array.node_count]) |node| {
|
|
152
|
-
node_pool.?.release(
|
|
153
|
-
@ptrCast(NodePool.Node, @alignCast(NodePool.node_alignment, node.?)),
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
allocator.free(array.nodes);
|
|
157
|
-
allocator.free(array.indexes);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
pub fn verify(array: Self) void {
|
|
161
|
-
assert(array.node_count <= node_count_max);
|
|
162
|
-
for (array.nodes) |node, node_index| {
|
|
163
|
-
if (node_index < array.node_count) {
|
|
164
|
-
// The first node_count pointers are non-null.
|
|
165
|
-
assert(node != null);
|
|
166
|
-
} else {
|
|
167
|
-
// The rest are non-null.
|
|
168
|
-
assert(node == null);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
for (array.nodes[0..array.node_count]) |_, node_index| {
|
|
172
|
-
const c = array.count(@intCast(u32, node_index));
|
|
173
|
-
// Every node is at most full.
|
|
174
|
-
assert(c <= node_capacity);
|
|
175
|
-
// Every node is at least half-full, except the last.
|
|
176
|
-
if (node_index < array.node_count - 1) {
|
|
177
|
-
assert(c >= @divTrunc(node_capacity, 2));
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
if (Key) |K| {
|
|
181
|
-
// If Key is not null then the elements must be sorted by key_from_value (but not necessarily unique).
|
|
182
|
-
var key_prior_or_null: ?K = null;
|
|
183
|
-
for (array.nodes[0..array.node_count]) |_, node_index| {
|
|
184
|
-
for (array.node_elements(@intCast(u32, node_index))) |*value| {
|
|
185
|
-
const key = key_from_value(value);
|
|
186
|
-
if (key_prior_or_null) |key_prior| {
|
|
187
|
-
assert(compare_keys(key_prior, key) != .gt);
|
|
188
|
-
}
|
|
189
|
-
key_prior_or_null = key;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
pub usingnamespace if (Key) |_| struct {
|
|
196
|
-
/// Returns the absolute index of the element being inserted.
|
|
197
|
-
pub fn insert_element(
|
|
198
|
-
array: *Self,
|
|
199
|
-
node_pool: *NodePool,
|
|
200
|
-
element: T,
|
|
201
|
-
) u32 {
|
|
202
|
-
if (options.verify) array.verify();
|
|
203
|
-
|
|
204
|
-
const cursor = array.search(key_from_value(&element));
|
|
205
|
-
const absolute_index = array.absolute_index_for_cursor(cursor);
|
|
206
|
-
array.insert_elements_at_absolute_index(node_pool, absolute_index, &[_]T{element});
|
|
207
|
-
|
|
208
|
-
if (options.verify) array.verify();
|
|
209
|
-
|
|
210
|
-
return absolute_index;
|
|
211
|
-
}
|
|
212
|
-
} else struct {
|
|
213
|
-
pub fn insert_elements(
|
|
214
|
-
array: *Self,
|
|
215
|
-
node_pool: *NodePool,
|
|
216
|
-
absolute_index: u32,
|
|
217
|
-
elements: []const T,
|
|
218
|
-
) void {
|
|
219
|
-
if (options.verify) array.verify();
|
|
220
|
-
|
|
221
|
-
array.insert_elements_at_absolute_index(
|
|
222
|
-
node_pool,
|
|
223
|
-
absolute_index,
|
|
224
|
-
elements,
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
if (options.verify) array.verify();
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
fn insert_elements_at_absolute_index(
|
|
232
|
-
array: *Self,
|
|
233
|
-
node_pool: *NodePool,
|
|
234
|
-
absolute_index: u32,
|
|
235
|
-
elements: []const T,
|
|
236
|
-
) void {
|
|
237
|
-
assert(elements.len > 0);
|
|
238
|
-
assert(absolute_index + elements.len <= element_count_max);
|
|
239
|
-
|
|
240
|
-
var i: u32 = 0;
|
|
241
|
-
while (i < elements.len) {
|
|
242
|
-
const batch = math.min(node_capacity, elements.len - i);
|
|
243
|
-
array.insert_elements_batch(
|
|
244
|
-
node_pool,
|
|
245
|
-
absolute_index + i,
|
|
246
|
-
elements[i..][0..batch],
|
|
247
|
-
);
|
|
248
|
-
i += batch;
|
|
249
|
-
}
|
|
250
|
-
assert(i == elements.len);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
fn insert_elements_batch(
|
|
254
|
-
array: *Self,
|
|
255
|
-
node_pool: *NodePool,
|
|
256
|
-
absolute_index: u32,
|
|
257
|
-
elements: []const T,
|
|
258
|
-
) void {
|
|
259
|
-
assert(elements.len > 0);
|
|
260
|
-
assert(elements.len <= node_capacity);
|
|
261
|
-
assert(absolute_index + elements.len <= element_count_max);
|
|
262
|
-
|
|
263
|
-
if (array.node_count == 0) {
|
|
264
|
-
assert(absolute_index == 0);
|
|
265
|
-
|
|
266
|
-
array.insert_empty_node_at(node_pool, 0);
|
|
267
|
-
|
|
268
|
-
assert(array.node_count == 1);
|
|
269
|
-
assert(array.nodes[0] != null);
|
|
270
|
-
assert(array.indexes[0] == 0);
|
|
271
|
-
assert(array.indexes[1] == 0);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const cursor = array.cursor_for_absolute_index(absolute_index);
|
|
275
|
-
assert(cursor.node < array.node_count);
|
|
276
|
-
|
|
277
|
-
const a = cursor.node;
|
|
278
|
-
const a_pointer = array.nodes[a].?;
|
|
279
|
-
assert(cursor.relative_index <= array.count(a));
|
|
280
|
-
|
|
281
|
-
const total = array.count(a) + @intCast(u32, elements.len);
|
|
282
|
-
if (total <= node_capacity) {
|
|
283
|
-
stdx.copy_right(
|
|
284
|
-
.inexact,
|
|
285
|
-
T,
|
|
286
|
-
a_pointer[cursor.relative_index + elements.len ..],
|
|
287
|
-
a_pointer[cursor.relative_index..array.count(a)],
|
|
288
|
-
);
|
|
289
|
-
stdx.copy_disjoint(.inexact, T, a_pointer[cursor.relative_index..], elements);
|
|
290
|
-
|
|
291
|
-
array.increment_indexes_after(a, @intCast(u32, elements.len));
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Insert a new node after the node being split.
|
|
296
|
-
const b = a + 1;
|
|
297
|
-
array.insert_empty_node_at(node_pool, b);
|
|
298
|
-
const b_pointer = array.nodes[b].?;
|
|
299
|
-
|
|
300
|
-
const a_half = div_ceil(total, 2);
|
|
301
|
-
const b_half = total - a_half;
|
|
302
|
-
assert(a_half >= b_half);
|
|
303
|
-
assert(a_half + b_half == total);
|
|
304
|
-
|
|
305
|
-
// The 1st case can be seen as a special case of the 2nd.
|
|
306
|
-
// The 5th case can be seen as a special case of the 4th.
|
|
307
|
-
//
|
|
308
|
-
// elements: [yyyyyy], relative_index: 0
|
|
309
|
-
// [xxxxx_][______]
|
|
310
|
-
// [______][xxxxx_] // after first copy_backwards
|
|
311
|
-
// [______][xxxxx_] // skip mem.copyBackwards (a_half >= relative_index)
|
|
312
|
-
// [yyyyyy][xxxxx_] // after second copy_backwards
|
|
313
|
-
//
|
|
314
|
-
// elements: [yy], relative_index: 1
|
|
315
|
-
// [xxxxx_][______]
|
|
316
|
-
// [x__x__][xxx___] // after first copy_backwards
|
|
317
|
-
// [x__x__][xxx___] // skip mem.copyBackwards (a_half >= relative_index)
|
|
318
|
-
// [xyyx__][xxx___] // after second copy_backwards
|
|
319
|
-
//
|
|
320
|
-
// elements: [yy], relative_index: 2
|
|
321
|
-
// [xxx_][____]
|
|
322
|
-
// [xx__][_x__] // after first copy_backwards
|
|
323
|
-
// [xx__][_x__] // skip mem.copyBackwards (a_half >= relative_index)
|
|
324
|
-
// [xxy_][yx__] // after second copy_backwards
|
|
325
|
-
//
|
|
326
|
-
// elements: [yy], relative_index: 5
|
|
327
|
-
// [xxxxxx][______]
|
|
328
|
-
// [xxxxx_][___x__] // after first copy_backwards
|
|
329
|
-
// [xxxx__][x__x__] // after mem.copyBackwards (a_half < relative_index)
|
|
330
|
-
// [xxxx__][xyyx__] // after second copy_backwards
|
|
331
|
-
//
|
|
332
|
-
// elements: [yyyyy_], relative_index: 5
|
|
333
|
-
// [xxxxx_][______]
|
|
334
|
-
// [xxxxx_][______] // after first copy_backwards
|
|
335
|
-
// [xxxxx_][______] // skip mem.copyBackwards (a_half >= relative_index)
|
|
336
|
-
// [xxxxx_][yyyyy_] // after second copy_backwards
|
|
337
|
-
|
|
338
|
-
const a_half_pointer = a_pointer[0..a_half];
|
|
339
|
-
const b_half_pointer = b_pointer[0..b_half];
|
|
340
|
-
|
|
341
|
-
// Move part of `a` forwards to make space for elements.
|
|
342
|
-
copy_backwards(
|
|
343
|
-
a_half_pointer,
|
|
344
|
-
b_half_pointer,
|
|
345
|
-
cursor.relative_index + elements.len,
|
|
346
|
-
a_pointer[cursor.relative_index..array.count(a)],
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
if (a_half < cursor.relative_index) {
|
|
350
|
-
// Move the part of `a` that is past the half-way point into `b`.
|
|
351
|
-
stdx.copy_right(
|
|
352
|
-
.inexact,
|
|
353
|
-
T,
|
|
354
|
-
b_half_pointer,
|
|
355
|
-
a_pointer[a_half..cursor.relative_index],
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// Move `elements` into `a` and/or `b`.
|
|
360
|
-
copy_backwards(
|
|
361
|
-
a_half_pointer,
|
|
362
|
-
b_half_pointer,
|
|
363
|
-
cursor.relative_index,
|
|
364
|
-
elements,
|
|
365
|
-
);
|
|
366
|
-
|
|
367
|
-
array.indexes[b] = array.indexes[a] + a_half;
|
|
368
|
-
array.increment_indexes_after(b, @intCast(u32, elements.len));
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/// Behaves like mem.copyBackwards, but as if `a` and `b` were a single contiguous slice.
|
|
372
|
-
/// `target` is the destination index within the concatenation of `a` and `b`.
|
|
373
|
-
fn copy_backwards(
|
|
374
|
-
a: []T,
|
|
375
|
-
b: []T,
|
|
376
|
-
target: usize,
|
|
377
|
-
source: []const T,
|
|
378
|
-
) void {
|
|
379
|
-
assert(target + source.len <= a.len + b.len);
|
|
380
|
-
const target_a = a[@minimum(target, a.len)..@minimum(target + source.len, a.len)];
|
|
381
|
-
const target_b = b[target -| a.len..(target + source.len) -| a.len];
|
|
382
|
-
assert(target_a.len + target_b.len == source.len);
|
|
383
|
-
const source_a = source[0..target_a.len];
|
|
384
|
-
const source_b = source[target_a.len..];
|
|
385
|
-
if (target_b.ptr != source_b.ptr) {
|
|
386
|
-
stdx.copy_right(.exact, T, target_b, source_b);
|
|
387
|
-
}
|
|
388
|
-
if (target_a.ptr != source_a.ptr) {
|
|
389
|
-
stdx.copy_right(.exact, T, target_a, source_a);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/// Insert an empty node at index `node`.
|
|
394
|
-
fn insert_empty_node_at(array: *Self, node_pool: *NodePool, node: u32) void {
|
|
395
|
-
assert(node <= array.node_count);
|
|
396
|
-
assert(array.node_count + 1 <= node_count_max);
|
|
397
|
-
|
|
398
|
-
stdx.copy_right(
|
|
399
|
-
.exact,
|
|
400
|
-
?*[node_capacity]T,
|
|
401
|
-
array.nodes[node + 1 .. array.node_count + 1],
|
|
402
|
-
array.nodes[node..array.node_count],
|
|
403
|
-
);
|
|
404
|
-
stdx.copy_right(
|
|
405
|
-
.exact,
|
|
406
|
-
u32,
|
|
407
|
-
array.indexes[node + 1 .. array.node_count + 2],
|
|
408
|
-
array.indexes[node .. array.node_count + 1],
|
|
409
|
-
);
|
|
410
|
-
|
|
411
|
-
array.node_count += 1;
|
|
412
|
-
const node_pointer = node_pool.acquire();
|
|
413
|
-
comptime {
|
|
414
|
-
// @ptrCast does not check that the size or alignment agree
|
|
415
|
-
assert(std.meta.alignment(@TypeOf(node_pointer)) >= @alignOf(T));
|
|
416
|
-
assert(@sizeOf(@TypeOf(node_pointer.*)) >= @sizeOf([node_capacity]T));
|
|
417
|
-
}
|
|
418
|
-
array.nodes[node] = @ptrCast(*[node_capacity]T, node_pointer);
|
|
419
|
-
assert(array.indexes[node] == array.indexes[node + 1]);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
pub fn remove_elements(
|
|
423
|
-
array: *Self,
|
|
424
|
-
node_pool: *NodePool,
|
|
425
|
-
absolute_index: u32,
|
|
426
|
-
remove_count: u32,
|
|
427
|
-
) void {
|
|
428
|
-
if (options.verify) array.verify();
|
|
429
|
-
|
|
430
|
-
assert(array.node_count > 0);
|
|
431
|
-
assert(remove_count > 0);
|
|
432
|
-
assert(absolute_index + remove_count <= element_count_max);
|
|
433
|
-
assert(absolute_index + remove_count <= array.indexes[array.node_count]);
|
|
434
|
-
|
|
435
|
-
const half = @divExact(node_capacity, 2);
|
|
436
|
-
|
|
437
|
-
var i: u32 = remove_count;
|
|
438
|
-
while (i > 0) {
|
|
439
|
-
const batch = math.min(half, i);
|
|
440
|
-
array.remove_elements_batch(node_pool, absolute_index, batch);
|
|
441
|
-
i -= batch;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (options.verify) array.verify();
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
fn remove_elements_batch(
|
|
448
|
-
array: *Self,
|
|
449
|
-
node_pool: *NodePool,
|
|
450
|
-
absolute_index: u32,
|
|
451
|
-
remove_count: u32,
|
|
452
|
-
) void {
|
|
453
|
-
assert(array.node_count > 0);
|
|
454
|
-
|
|
455
|
-
// Restricting the batch size to half node capacity ensures that elements
|
|
456
|
-
// are removed from at most two nodes.
|
|
457
|
-
const half = @divExact(node_capacity, 2);
|
|
458
|
-
assert(remove_count <= half);
|
|
459
|
-
assert(remove_count > 0);
|
|
460
|
-
|
|
461
|
-
assert(absolute_index + remove_count <= element_count_max);
|
|
462
|
-
assert(absolute_index + remove_count <= array.indexes[array.node_count]);
|
|
463
|
-
|
|
464
|
-
const cursor = array.cursor_for_absolute_index(absolute_index);
|
|
465
|
-
assert(cursor.node < array.node_count);
|
|
466
|
-
|
|
467
|
-
const a = cursor.node;
|
|
468
|
-
const a_pointer = array.nodes[a].?;
|
|
469
|
-
const a_remaining = cursor.relative_index;
|
|
470
|
-
|
|
471
|
-
// Remove elements from exactly one node:
|
|
472
|
-
if (a_remaining + remove_count <= array.count(a)) {
|
|
473
|
-
stdx.copy_left(
|
|
474
|
-
.inexact,
|
|
475
|
-
T,
|
|
476
|
-
a_pointer[a_remaining..],
|
|
477
|
-
a_pointer[a_remaining + remove_count .. array.count(a)],
|
|
478
|
-
);
|
|
479
|
-
|
|
480
|
-
array.decrement_indexes_after(a, remove_count);
|
|
481
|
-
|
|
482
|
-
array.maybe_remove_or_merge_node_with_next(node_pool, a);
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Remove elements from exactly two nodes:
|
|
487
|
-
|
|
488
|
-
const b = a + 1;
|
|
489
|
-
const b_pointer = array.nodes[b].?;
|
|
490
|
-
const b_remaining = b_pointer[remove_count -
|
|
491
|
-
(array.count(a) - a_remaining) .. array.count(b)];
|
|
492
|
-
|
|
493
|
-
assert(@ptrToInt(b_remaining.ptr) > @ptrToInt(b_pointer));
|
|
494
|
-
|
|
495
|
-
// Only one of these nodes may become empty, as we limit batch size to
|
|
496
|
-
// half node capacity.
|
|
497
|
-
assert(a_remaining > 0 or b_remaining.len > 0);
|
|
498
|
-
|
|
499
|
-
if (a_remaining >= half) {
|
|
500
|
-
stdx.copy_left(.inexact, T, b_pointer, b_remaining);
|
|
501
|
-
|
|
502
|
-
array.indexes[b] = array.indexes[a] + a_remaining;
|
|
503
|
-
array.decrement_indexes_after(b, remove_count);
|
|
504
|
-
|
|
505
|
-
array.maybe_remove_or_merge_node_with_next(node_pool, b);
|
|
506
|
-
} else if (b_remaining.len >= half) {
|
|
507
|
-
assert(a_remaining < half);
|
|
508
|
-
|
|
509
|
-
array.indexes[b] = array.indexes[a] + a_remaining;
|
|
510
|
-
array.decrement_indexes_after(b, remove_count);
|
|
511
|
-
|
|
512
|
-
array.maybe_merge_nodes(node_pool, a, b_remaining);
|
|
513
|
-
} else {
|
|
514
|
-
assert(a_remaining < half and b_remaining.len < half);
|
|
515
|
-
assert(a_remaining + b_remaining.len <= node_capacity);
|
|
516
|
-
|
|
517
|
-
stdx.copy_disjoint(.inexact, T, a_pointer[a_remaining..], b_remaining);
|
|
518
|
-
|
|
519
|
-
array.indexes[b] = array.indexes[a] + a_remaining + @intCast(u32, b_remaining.len);
|
|
520
|
-
array.decrement_indexes_after(b, remove_count);
|
|
521
|
-
|
|
522
|
-
array.remove_empty_node_at(node_pool, b);
|
|
523
|
-
|
|
524
|
-
// Either:
|
|
525
|
-
// * `b` was the last node so now `a` is the last node
|
|
526
|
-
// * both `a` and `b` were at least half-full so now `a` is at least half-full
|
|
527
|
-
assert(b == array.node_count or array.count(a) >= half);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
fn maybe_remove_or_merge_node_with_next(
|
|
532
|
-
array: *Self,
|
|
533
|
-
node_pool: *NodePool,
|
|
534
|
-
node: u32,
|
|
535
|
-
) void {
|
|
536
|
-
assert(node < array.node_count);
|
|
537
|
-
|
|
538
|
-
if (array.count(node) == 0) {
|
|
539
|
-
array.remove_empty_node_at(node_pool, node);
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
if (node == array.node_count - 1) return;
|
|
544
|
-
|
|
545
|
-
const next_elements = array.nodes[node + 1].?[0..array.count(node + 1)];
|
|
546
|
-
array.maybe_merge_nodes(node_pool, node, next_elements);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
fn maybe_merge_nodes(
|
|
550
|
-
array: *Self,
|
|
551
|
-
node_pool: *NodePool,
|
|
552
|
-
node: u32,
|
|
553
|
-
elements_next_node: []T,
|
|
554
|
-
) void {
|
|
555
|
-
const half = @divExact(node_capacity, 2);
|
|
556
|
-
|
|
557
|
-
const a = node;
|
|
558
|
-
const a_pointer = array.nodes[a].?;
|
|
559
|
-
assert(array.count(a) <= node_capacity);
|
|
560
|
-
|
|
561
|
-
// The elements_next_node slice may not be at the start of the node,
|
|
562
|
-
// but the length of the slice will match count(b).
|
|
563
|
-
const b = a + 1;
|
|
564
|
-
const b_pointer = array.nodes[b].?;
|
|
565
|
-
const b_elements = elements_next_node;
|
|
566
|
-
assert(b_elements.len == array.count(b));
|
|
567
|
-
assert(b_elements.len > 0);
|
|
568
|
-
assert(b_elements.len >= half or b == array.node_count - 1);
|
|
569
|
-
assert(b_elements.len <= node_capacity);
|
|
570
|
-
assert(@ptrToInt(b_elements.ptr) >= @ptrToInt(b_pointer));
|
|
571
|
-
|
|
572
|
-
// Our function would still be correct if this assert fails, but we would
|
|
573
|
-
// unnecessarily copy all elements of b to node a and then delete b
|
|
574
|
-
// instead of simply deleting a.
|
|
575
|
-
assert(!(array.count(a) == 0 and b_pointer == b_elements.ptr));
|
|
576
|
-
|
|
577
|
-
const total = array.count(a) + @intCast(u32, b_elements.len);
|
|
578
|
-
if (total <= node_capacity) {
|
|
579
|
-
stdx.copy_disjoint(.inexact, T, a_pointer[array.count(a)..], b_elements);
|
|
580
|
-
|
|
581
|
-
array.indexes[b] = array.indexes[b + 1];
|
|
582
|
-
array.remove_empty_node_at(node_pool, b);
|
|
583
|
-
|
|
584
|
-
assert(array.count(a) >= half or a == array.node_count - 1);
|
|
585
|
-
} else if (array.count(a) < half) {
|
|
586
|
-
const a_half = div_ceil(total, 2);
|
|
587
|
-
const b_half = total - a_half;
|
|
588
|
-
assert(a_half >= b_half);
|
|
589
|
-
assert(a_half + b_half == total);
|
|
590
|
-
|
|
591
|
-
stdx.copy_disjoint(
|
|
592
|
-
.exact,
|
|
593
|
-
T,
|
|
594
|
-
a_pointer[array.count(a)..a_half],
|
|
595
|
-
b_elements[0 .. a_half - array.count(a)],
|
|
596
|
-
);
|
|
597
|
-
stdx.copy_left(.inexact, T, b_pointer, b_elements[a_half - array.count(a) ..]);
|
|
598
|
-
|
|
599
|
-
array.indexes[b] = array.indexes[a] + a_half;
|
|
600
|
-
|
|
601
|
-
assert(array.count(a) >= half);
|
|
602
|
-
assert(array.count(b) >= half);
|
|
603
|
-
} else {
|
|
604
|
-
assert(b_pointer == b_elements.ptr);
|
|
605
|
-
assert(array.indexes[b] + b_elements.len == array.indexes[b + 1]);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
/// Remove an empty node at index `node`.
|
|
610
|
-
fn remove_empty_node_at(array: *Self, node_pool: *NodePool, node: u32) void {
|
|
611
|
-
assert(array.node_count > 0);
|
|
612
|
-
assert(node < array.node_count);
|
|
613
|
-
assert(array.count(node) == 0);
|
|
614
|
-
|
|
615
|
-
node_pool.release(
|
|
616
|
-
@ptrCast(NodePool.Node, @alignCast(NodePool.node_alignment, array.nodes[node].?)),
|
|
617
|
-
);
|
|
618
|
-
|
|
619
|
-
stdx.copy_left(
|
|
620
|
-
.exact,
|
|
621
|
-
?*[node_capacity]T,
|
|
622
|
-
array.nodes[node .. array.node_count - 1],
|
|
623
|
-
array.nodes[node + 1 .. array.node_count],
|
|
624
|
-
);
|
|
625
|
-
stdx.copy_left(
|
|
626
|
-
.exact,
|
|
627
|
-
u32,
|
|
628
|
-
array.indexes[node..array.node_count],
|
|
629
|
-
array.indexes[node + 1 .. array.node_count + 1],
|
|
630
|
-
);
|
|
631
|
-
|
|
632
|
-
array.node_count -= 1;
|
|
633
|
-
array.nodes[array.node_count] = null;
|
|
634
|
-
array.indexes[array.node_count + 1] = undefined;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
inline fn count(array: Self, node: u32) u32 {
|
|
638
|
-
const result = array.indexes[node + 1] - array.indexes[node];
|
|
639
|
-
assert(result <= node_capacity);
|
|
640
|
-
return result;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
inline fn increment_indexes_after(array: *Self, node: u32, delta: u32) void {
|
|
644
|
-
for (array.indexes[node + 1 .. array.node_count + 1]) |*i| i.* += delta;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
inline fn decrement_indexes_after(array: *Self, node: u32, delta: u32) void {
|
|
648
|
-
for (array.indexes[node + 1 .. array.node_count + 1]) |*i| i.* -= delta;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
pub inline fn node_elements(array: Self, node: u32) []T {
|
|
652
|
-
assert(node < array.node_count);
|
|
653
|
-
return array.nodes[node].?[0..array.count(node)];
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
pub inline fn node_last_element(array: Self, node: u32) T {
|
|
657
|
-
return array.node_elements(node)[array.count(node) - 1];
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
pub inline fn element_at_cursor(array: Self, cursor: Cursor) T {
|
|
661
|
-
return array.node_elements(cursor.node)[cursor.relative_index];
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
pub inline fn first(_: Self) Cursor {
|
|
665
|
-
return .{
|
|
666
|
-
.node = 0,
|
|
667
|
-
.relative_index = 0,
|
|
668
|
-
};
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
pub inline fn last(array: Self) Cursor {
|
|
672
|
-
if (array.node_count == 0) return array.first();
|
|
673
|
-
|
|
674
|
-
return .{
|
|
675
|
-
.node = array.node_count - 1,
|
|
676
|
-
.relative_index = array.count(array.node_count - 1) - 1,
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
pub inline fn len(array: Self) u32 {
|
|
681
|
-
const result = array.indexes[array.node_count];
|
|
682
|
-
assert(result <= element_count_max);
|
|
683
|
-
return result;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
// TODO Consider enabling ReleaseFast for this once tested.
|
|
687
|
-
pub fn absolute_index_for_cursor(array: Self, cursor: Cursor) u32 {
|
|
688
|
-
if (array.node_count == 0) {
|
|
689
|
-
assert(cursor.node == 0);
|
|
690
|
-
assert(cursor.relative_index == 0);
|
|
691
|
-
return 0;
|
|
692
|
-
}
|
|
693
|
-
assert(cursor.node < array.node_count);
|
|
694
|
-
if (cursor.node == array.node_count - 1) {
|
|
695
|
-
// Insertion may target the index one past the end of the array.
|
|
696
|
-
assert(cursor.relative_index <= array.count(cursor.node));
|
|
697
|
-
} else {
|
|
698
|
-
assert(cursor.relative_index < array.count(cursor.node));
|
|
699
|
-
}
|
|
700
|
-
return array.indexes[cursor.node] + cursor.relative_index;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
fn cursor_for_absolute_index(array: Self, absolute_index: u32) Cursor {
|
|
704
|
-
// This function could handle node_count == 0 by returning a zero Cursor.
|
|
705
|
-
// However, this is an internal function and we don't require this behavior.
|
|
706
|
-
assert(array.node_count > 0);
|
|
707
|
-
|
|
708
|
-
assert(absolute_index < element_count_max);
|
|
709
|
-
assert(absolute_index <= array.len());
|
|
710
|
-
|
|
711
|
-
const result = binary_search_keys(u32, struct {
|
|
712
|
-
inline fn compare(a: u32, b: u32) math.Order {
|
|
713
|
-
return math.order(a, b);
|
|
714
|
-
}
|
|
715
|
-
}.compare, array.indexes[0..array.node_count], absolute_index, .{});
|
|
716
|
-
|
|
717
|
-
if (result.exact) {
|
|
718
|
-
return .{
|
|
719
|
-
.node = result.index,
|
|
720
|
-
.relative_index = 0,
|
|
721
|
-
};
|
|
722
|
-
} else {
|
|
723
|
-
const node = result.index - 1;
|
|
724
|
-
const relative_index = absolute_index - array.indexes[node];
|
|
725
|
-
if (node == array.node_count - 1) {
|
|
726
|
-
// Insertion may target the index one past the end of the array.
|
|
727
|
-
assert(relative_index <= array.count(node));
|
|
728
|
-
} else {
|
|
729
|
-
assert(relative_index < array.count(node));
|
|
730
|
-
}
|
|
731
|
-
return .{
|
|
732
|
-
.node = node,
|
|
733
|
-
.relative_index = relative_index,
|
|
734
|
-
};
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
pub const Iterator = struct {
|
|
739
|
-
array: *const Self,
|
|
740
|
-
direction: Direction,
|
|
741
|
-
|
|
742
|
-
cursor: Cursor,
|
|
743
|
-
|
|
744
|
-
/// The user may set this early to stop iteration. For example,
|
|
745
|
-
/// if the returned table info is outside the key range.
|
|
746
|
-
done: bool = false,
|
|
747
|
-
|
|
748
|
-
pub fn next(it: *Iterator) ?*const T {
|
|
749
|
-
if (it.done) return null;
|
|
750
|
-
|
|
751
|
-
assert(it.cursor.node < it.array.node_count);
|
|
752
|
-
|
|
753
|
-
const elements = it.array.node_elements(it.cursor.node);
|
|
754
|
-
const element = &elements[it.cursor.relative_index];
|
|
755
|
-
|
|
756
|
-
switch (it.direction) {
|
|
757
|
-
.ascending => {
|
|
758
|
-
if (it.cursor.relative_index == elements.len - 1) {
|
|
759
|
-
if (it.cursor.node == it.array.node_count - 1) {
|
|
760
|
-
it.done = true;
|
|
761
|
-
} else {
|
|
762
|
-
it.cursor.node += 1;
|
|
763
|
-
it.cursor.relative_index = 0;
|
|
764
|
-
}
|
|
765
|
-
} else {
|
|
766
|
-
it.cursor.relative_index += 1;
|
|
767
|
-
}
|
|
768
|
-
},
|
|
769
|
-
.descending => {
|
|
770
|
-
if (it.cursor.relative_index == 0) {
|
|
771
|
-
if (it.cursor.node == 0) {
|
|
772
|
-
it.done = true;
|
|
773
|
-
} else {
|
|
774
|
-
it.cursor.node -= 1;
|
|
775
|
-
it.cursor.relative_index = it.array.count(it.cursor.node) - 1;
|
|
776
|
-
}
|
|
777
|
-
} else {
|
|
778
|
-
it.cursor.relative_index -= 1;
|
|
779
|
-
}
|
|
780
|
-
},
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
return element;
|
|
784
|
-
}
|
|
785
|
-
};
|
|
786
|
-
|
|
787
|
-
pub fn iterator_from_cursor(
|
|
788
|
-
array: *const Self,
|
|
789
|
-
/// First element of iteration.
|
|
790
|
-
cursor: Cursor,
|
|
791
|
-
direction: Direction,
|
|
792
|
-
) Iterator {
|
|
793
|
-
if (array.node_count == 0) {
|
|
794
|
-
assert(cursor.node == 0);
|
|
795
|
-
assert(cursor.relative_index == 0);
|
|
796
|
-
|
|
797
|
-
return Iterator{
|
|
798
|
-
.array = array,
|
|
799
|
-
.direction = direction,
|
|
800
|
-
.cursor = .{ .node = 0, .relative_index = 0 },
|
|
801
|
-
.done = true,
|
|
802
|
-
};
|
|
803
|
-
} else {
|
|
804
|
-
assert(cursor.node < array.node_count);
|
|
805
|
-
assert(cursor.relative_index < array.count(cursor.node));
|
|
806
|
-
|
|
807
|
-
return .{
|
|
808
|
-
.array = array,
|
|
809
|
-
.direction = direction,
|
|
810
|
-
.cursor = cursor,
|
|
811
|
-
};
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
pub fn iterator_from_index(
|
|
816
|
-
array: *const Self,
|
|
817
|
-
/// First element of iteration.
|
|
818
|
-
absolute_index: u32,
|
|
819
|
-
direction: Direction,
|
|
820
|
-
) Iterator {
|
|
821
|
-
assert(absolute_index < element_count_max);
|
|
822
|
-
|
|
823
|
-
if (array.node_count == 0) {
|
|
824
|
-
assert(absolute_index == 0);
|
|
825
|
-
|
|
826
|
-
return Iterator{
|
|
827
|
-
.array = array,
|
|
828
|
-
.direction = direction,
|
|
829
|
-
.cursor = .{ .node = 0, .relative_index = 0 },
|
|
830
|
-
.done = true,
|
|
831
|
-
};
|
|
832
|
-
} else {
|
|
833
|
-
assert(absolute_index < array.len());
|
|
834
|
-
|
|
835
|
-
return Iterator{
|
|
836
|
-
.array = array,
|
|
837
|
-
.direction = direction,
|
|
838
|
-
.cursor = array.cursor_for_absolute_index(absolute_index),
|
|
839
|
-
};
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
pub usingnamespace if (Key) |K| struct {
|
|
844
|
-
/// Returns a cursor to the index of the key either exactly equal to the target key or,
|
|
845
|
-
/// if there is no exact match, the next greatest key.
|
|
846
|
-
pub fn search(
|
|
847
|
-
array: *const Self,
|
|
848
|
-
key: K,
|
|
849
|
-
) Cursor {
|
|
850
|
-
if (array.node_count == 0) {
|
|
851
|
-
return .{
|
|
852
|
-
.node = 0,
|
|
853
|
-
.relative_index = 0,
|
|
854
|
-
};
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
var offset: usize = 0;
|
|
858
|
-
var length: usize = array.node_count;
|
|
859
|
-
while (length > 1) {
|
|
860
|
-
const half = length / 2;
|
|
861
|
-
const mid = offset + half;
|
|
862
|
-
|
|
863
|
-
const node = &array.nodes[mid].?[0];
|
|
864
|
-
// This trick seems to be what's needed to get llvm to emit branchless code for this,
|
|
865
|
-
// a ternary-style if expression was generated as a jump here for whatever reason.
|
|
866
|
-
const next_offsets = [_]usize{ offset, mid };
|
|
867
|
-
offset = next_offsets[@boolToInt(compare_keys(key_from_value(node), key) == .lt)];
|
|
868
|
-
|
|
869
|
-
length -= half;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
// Unlike a normal binary search, don't increment the offset when "key" is higher
|
|
873
|
-
// than the element — "round down" to the previous node.
|
|
874
|
-
// This guarantees that the node result is never "== node_count".
|
|
875
|
-
//
|
|
876
|
-
// (If there are two adjacent nodes starting with keys A and C, and we search B,
|
|
877
|
-
// we want to pick the A node.)
|
|
878
|
-
const node = @intCast(u32, offset);
|
|
879
|
-
assert(node < array.node_count);
|
|
880
|
-
|
|
881
|
-
const relative_index = binary_search_values_raw(
|
|
882
|
-
K,
|
|
883
|
-
T,
|
|
884
|
-
key_from_value,
|
|
885
|
-
compare_keys,
|
|
886
|
-
array.node_elements(node),
|
|
887
|
-
key,
|
|
888
|
-
.{},
|
|
889
|
-
);
|
|
890
|
-
|
|
891
|
-
// Follow the same rule as absolute_index_for_cursor:
|
|
892
|
-
// only return relative_index==array.count() at the last node.
|
|
893
|
-
if (node + 1 < array.node_count and
|
|
894
|
-
relative_index == array.count(node))
|
|
895
|
-
{
|
|
896
|
-
return .{
|
|
897
|
-
.node = node + 1,
|
|
898
|
-
.relative_index = 0,
|
|
899
|
-
};
|
|
900
|
-
} else {
|
|
901
|
-
return .{
|
|
902
|
-
.node = node,
|
|
903
|
-
.relative_index = relative_index,
|
|
904
|
-
};
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
} else struct {};
|
|
908
|
-
};
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
fn TestContext(
|
|
912
|
-
comptime T: type,
|
|
913
|
-
comptime node_size: u32,
|
|
914
|
-
comptime element_count_max: u32,
|
|
915
|
-
comptime Key: type,
|
|
916
|
-
comptime key_from_value: fn (*const T) callconv(.Inline) Key,
|
|
917
|
-
comptime compare_keys: fn (Key, Key) callconv(.Inline) math.Order,
|
|
918
|
-
comptime element_order: enum { sorted, unsorted },
|
|
919
|
-
comptime options: Options,
|
|
920
|
-
) type {
|
|
921
|
-
return struct {
|
|
922
|
-
const Self = @This();
|
|
923
|
-
|
|
924
|
-
const testing = std.testing;
|
|
925
|
-
const log = false;
|
|
926
|
-
|
|
927
|
-
const NodePool = @import("node_pool.zig").NodePool;
|
|
928
|
-
|
|
929
|
-
// Test overaligned nodes to catch compile errors for missing @alignCast()
|
|
930
|
-
const TestPool = NodePool(node_size, 2 * @alignOf(T));
|
|
931
|
-
const TestArray = switch (element_order) {
|
|
932
|
-
.sorted => SortedSegmentedArray(T, TestPool, element_count_max, Key, key_from_value, compare_keys, options),
|
|
933
|
-
.unsorted => SegmentedArray(T, TestPool, element_count_max, options),
|
|
934
|
-
};
|
|
935
|
-
|
|
936
|
-
random: std.rand.Random,
|
|
937
|
-
|
|
938
|
-
pool: TestPool,
|
|
939
|
-
array: TestArray,
|
|
940
|
-
|
|
941
|
-
reference: std.ArrayList(T),
|
|
942
|
-
|
|
943
|
-
inserts: u64 = 0,
|
|
944
|
-
removes: u64 = 0,
|
|
945
|
-
|
|
946
|
-
fn init(random: std.rand.Random) !Self {
|
|
947
|
-
var pool = try TestPool.init(testing.allocator, TestArray.node_count_max);
|
|
948
|
-
errdefer pool.deinit(testing.allocator);
|
|
949
|
-
|
|
950
|
-
var array = try TestArray.init(testing.allocator);
|
|
951
|
-
errdefer array.deinit(testing.allocator, &pool);
|
|
952
|
-
|
|
953
|
-
var reference = std.ArrayList(T).init(testing.allocator);
|
|
954
|
-
errdefer reference.deinit();
|
|
955
|
-
|
|
956
|
-
try reference.ensureTotalCapacity(element_count_max);
|
|
957
|
-
|
|
958
|
-
return Self{
|
|
959
|
-
.random = random,
|
|
960
|
-
.pool = pool,
|
|
961
|
-
.array = array,
|
|
962
|
-
.reference = reference,
|
|
963
|
-
};
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
fn deinit(context: *Self) void {
|
|
967
|
-
context.array.deinit(testing.allocator, &context.pool);
|
|
968
|
-
context.pool.deinit(testing.allocator);
|
|
969
|
-
|
|
970
|
-
context.reference.deinit();
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
fn run(context: *Self) !void {
|
|
974
|
-
{
|
|
975
|
-
var i: usize = 0;
|
|
976
|
-
while (i < element_count_max * 2) : (i += 1) {
|
|
977
|
-
switch (context.random.uintLessThanBiased(u32, 100)) {
|
|
978
|
-
0...59 => try context.insert(),
|
|
979
|
-
60...99 => try context.remove(),
|
|
980
|
-
else => unreachable,
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
{
|
|
986
|
-
var i: usize = 0;
|
|
987
|
-
while (i < element_count_max * 2) : (i += 1) {
|
|
988
|
-
switch (context.random.uintLessThanBiased(u32, 100)) {
|
|
989
|
-
0...39 => try context.insert(),
|
|
990
|
-
40...99 => try context.remove(),
|
|
991
|
-
else => unreachable,
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
try context.remove_all();
|
|
997
|
-
|
|
998
|
-
if (element_order == .unsorted) {
|
|
999
|
-
// Insert at the beginning of the array until the array is full.
|
|
1000
|
-
while (context.array.len() < element_count_max) {
|
|
1001
|
-
try context.insert_before_first();
|
|
1002
|
-
}
|
|
1003
|
-
assert(context.array.node_count >= TestArray.node_count_max - 1);
|
|
1004
|
-
|
|
1005
|
-
// Remove all-but-one elements from the last node and insert them into the first node.
|
|
1006
|
-
const element_count_last = context.array.count(context.array.node_count - 1);
|
|
1007
|
-
var element_index: usize = 0;
|
|
1008
|
-
while (element_index < element_count_last - 1) : (element_index += 1) {
|
|
1009
|
-
try context.remove_last();
|
|
1010
|
-
try context.insert_before_first();
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
// We should now have maxed out our node count.
|
|
1014
|
-
assert(context.array.node_count == TestArray.node_count_max);
|
|
1015
|
-
|
|
1016
|
-
try context.remove_all();
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
fn insert(context: *Self) !void {
|
|
1021
|
-
const reference_len = @intCast(u32, context.reference.items.len);
|
|
1022
|
-
const count_free = element_count_max - reference_len;
|
|
1023
|
-
|
|
1024
|
-
if (count_free == 0) return;
|
|
1025
|
-
|
|
1026
|
-
var buffer: [TestArray.node_capacity * 3]T = undefined;
|
|
1027
|
-
const count_max = @minimum(count_free, TestArray.node_capacity * 3);
|
|
1028
|
-
const count = context.random.uintAtMostBiased(u32, count_max - 1) + 1;
|
|
1029
|
-
context.random.bytes(mem.sliceAsBytes(buffer[0..count]));
|
|
1030
|
-
|
|
1031
|
-
assert(context.reference.items.len <= element_count_max);
|
|
1032
|
-
|
|
1033
|
-
switch (element_order) {
|
|
1034
|
-
.unsorted => {
|
|
1035
|
-
const index = context.random.uintAtMostBiased(u32, reference_len);
|
|
1036
|
-
|
|
1037
|
-
context.array.insert_elements(&context.pool, index, buffer[0..count]);
|
|
1038
|
-
// TODO the standard library could use an AssumeCapacity variant of this.
|
|
1039
|
-
context.reference.insertSlice(index, buffer[0..count]) catch unreachable;
|
|
1040
|
-
},
|
|
1041
|
-
.sorted => {
|
|
1042
|
-
for (buffer[0..count]) |value| {
|
|
1043
|
-
const index_actual = context.array.insert_element(&context.pool, value);
|
|
1044
|
-
const index_expect = context.reference_index(key_from_value(&value));
|
|
1045
|
-
context.reference.insert(index_expect, value) catch unreachable;
|
|
1046
|
-
try std.testing.expectEqual(index_expect, index_actual);
|
|
1047
|
-
}
|
|
1048
|
-
},
|
|
1049
|
-
}
|
|
1050
|
-
context.inserts += count;
|
|
1051
|
-
|
|
1052
|
-
try context.verify();
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
fn remove(context: *Self) !void {
|
|
1056
|
-
const reference_len = @intCast(u32, context.reference.items.len);
|
|
1057
|
-
if (reference_len == 0) return;
|
|
1058
|
-
|
|
1059
|
-
const count_max = @minimum(reference_len, TestArray.node_capacity * 3);
|
|
1060
|
-
const count = context.random.uintAtMostBiased(u32, count_max - 1) + 1;
|
|
1061
|
-
|
|
1062
|
-
assert(context.reference.items.len <= element_count_max);
|
|
1063
|
-
const index = context.random.uintAtMostBiased(u32, reference_len - count);
|
|
1064
|
-
|
|
1065
|
-
context.array.remove_elements(&context.pool, index, count);
|
|
1066
|
-
|
|
1067
|
-
context.reference.replaceRange(index, count, &[0]T{}) catch unreachable;
|
|
1068
|
-
|
|
1069
|
-
context.removes += count;
|
|
1070
|
-
|
|
1071
|
-
try context.verify();
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
fn insert_before_first(context: *Self) !void {
|
|
1075
|
-
assert(element_order == .unsorted);
|
|
1076
|
-
|
|
1077
|
-
const insert_index = context.array.absolute_index_for_cursor(context.array.first());
|
|
1078
|
-
|
|
1079
|
-
var element: T = undefined;
|
|
1080
|
-
context.random.bytes(mem.asBytes(&element));
|
|
1081
|
-
|
|
1082
|
-
context.array.insert_elements(&context.pool, insert_index, &.{element});
|
|
1083
|
-
context.reference.insert(insert_index, element) catch unreachable;
|
|
1084
|
-
|
|
1085
|
-
context.inserts += 1;
|
|
1086
|
-
|
|
1087
|
-
try context.verify();
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
fn remove_last(context: *Self) !void {
|
|
1091
|
-
assert(element_order == .unsorted);
|
|
1092
|
-
|
|
1093
|
-
const remove_index = context.array.absolute_index_for_cursor(context.array.last());
|
|
1094
|
-
|
|
1095
|
-
context.array.remove_elements(&context.pool, remove_index, 1);
|
|
1096
|
-
context.reference.replaceRange(remove_index, 1, &[0]T{}) catch unreachable;
|
|
1097
|
-
|
|
1098
|
-
context.removes += 1;
|
|
1099
|
-
|
|
1100
|
-
try context.verify();
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
fn remove_all(context: *Self) !void {
|
|
1104
|
-
while (context.reference.items.len > 0) try context.remove();
|
|
1105
|
-
|
|
1106
|
-
try testing.expectEqual(@as(u32, 0), context.array.len());
|
|
1107
|
-
try testing.expect(context.inserts > 0);
|
|
1108
|
-
try testing.expect(context.inserts == context.removes);
|
|
1109
|
-
|
|
1110
|
-
if (log) {
|
|
1111
|
-
std.debug.print("\ninserts: {}, removes: {}\n", .{
|
|
1112
|
-
context.inserts,
|
|
1113
|
-
context.removes,
|
|
1114
|
-
});
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
try context.verify();
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
fn verify(context: *Self) !void {
|
|
1121
|
-
if (log) {
|
|
1122
|
-
std.debug.print("expect: ", .{});
|
|
1123
|
-
for (context.reference.items) |i| std.debug.print("{}, ", .{i});
|
|
1124
|
-
|
|
1125
|
-
std.debug.print("\nactual: ", .{});
|
|
1126
|
-
var it = context.array.iterator_from_index(0, .ascending);
|
|
1127
|
-
while (it.next()) |i| std.debug.print("{}, ", .{i.*});
|
|
1128
|
-
std.debug.print("\n", .{});
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
try testing.expectEqual(context.reference.items.len, context.array.len());
|
|
1132
|
-
|
|
1133
|
-
{
|
|
1134
|
-
var it = context.array.iterator_from_index(0, .ascending);
|
|
1135
|
-
|
|
1136
|
-
for (context.reference.items) |expect| {
|
|
1137
|
-
const actual = it.next() orelse return error.TestUnexpectedResult;
|
|
1138
|
-
try testing.expectEqual(expect, actual.*);
|
|
1139
|
-
}
|
|
1140
|
-
try testing.expectEqual(@as(?*const T, null), it.next());
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
{
|
|
1144
|
-
var it = context.array.iterator_from_index(
|
|
1145
|
-
@intCast(u32, context.reference.items.len) -| 1,
|
|
1146
|
-
.descending,
|
|
1147
|
-
);
|
|
1148
|
-
|
|
1149
|
-
var i = context.reference.items.len;
|
|
1150
|
-
while (i > 0) {
|
|
1151
|
-
i -= 1;
|
|
1152
|
-
|
|
1153
|
-
const expect = context.reference.items[i];
|
|
1154
|
-
const actual = it.next() orelse return error.TestUnexpectedResult;
|
|
1155
|
-
try testing.expectEqual(expect, actual.*);
|
|
1156
|
-
}
|
|
1157
|
-
try testing.expectEqual(@as(?*const T, null), it.next());
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
{
|
|
1161
|
-
for (context.reference.items) |_, i| {
|
|
1162
|
-
try testing.expect(std.meta.eql(
|
|
1163
|
-
i,
|
|
1164
|
-
context.array.absolute_index_for_cursor(
|
|
1165
|
-
context.array.cursor_for_absolute_index(@intCast(u32, i)),
|
|
1166
|
-
),
|
|
1167
|
-
));
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
if (element_order == .sorted) {
|
|
1172
|
-
for (context.reference.items) |*expect, i| {
|
|
1173
|
-
if (i == 0) continue;
|
|
1174
|
-
try testing.expect(compare_keys(
|
|
1175
|
-
key_from_value(&context.reference.items[i - 1]),
|
|
1176
|
-
key_from_value(expect),
|
|
1177
|
-
) != .gt);
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
if (context.array.len() == 0) {
|
|
1182
|
-
try testing.expectEqual(@as(u32, 0), context.array.node_count);
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
for (context.array.nodes[context.array.node_count..]) |node| {
|
|
1186
|
-
try testing.expectEqual(@as(?*[TestArray.node_capacity]T, null), node);
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
{
|
|
1190
|
-
var i: u32 = 0;
|
|
1191
|
-
while (i < context.array.node_count -| 1) : (i += 1) {
|
|
1192
|
-
try testing.expect(context.array.count(i) >=
|
|
1193
|
-
@divExact(TestArray.node_capacity, 2));
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
if (element_order == .sorted) try context.verify_search();
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
fn verify_search(context: *Self) !void {
|
|
1200
|
-
var queries: [20]Key = undefined;
|
|
1201
|
-
context.random.bytes(mem.sliceAsBytes(&queries));
|
|
1202
|
-
|
|
1203
|
-
// Test min/max exceptional values on different SegmentedArray shapes.
|
|
1204
|
-
queries[0] = 0;
|
|
1205
|
-
queries[1] = math.maxInt(Key);
|
|
1206
|
-
|
|
1207
|
-
for (queries) |query| {
|
|
1208
|
-
try testing.expectEqual(
|
|
1209
|
-
context.reference_index(query),
|
|
1210
|
-
context.array.absolute_index_for_cursor(context.array.search(query)),
|
|
1211
|
-
);
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
fn reference_index(context: *const Self, key: Key) u32 {
|
|
1216
|
-
return binary_search_values_raw(
|
|
1217
|
-
Key,
|
|
1218
|
-
T,
|
|
1219
|
-
key_from_value,
|
|
1220
|
-
compare_keys,
|
|
1221
|
-
context.reference.items,
|
|
1222
|
-
key,
|
|
1223
|
-
.{},
|
|
1224
|
-
);
|
|
1225
|
-
}
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
pub fn run_tests(seed: u64, comptime options: Options) !void {
|
|
1230
|
-
var prng = std.rand.DefaultPrng.init(seed);
|
|
1231
|
-
const random = prng.random();
|
|
1232
|
-
|
|
1233
|
-
const Key = @import("composite_key.zig").CompositeKey(u64);
|
|
1234
|
-
const TableType = @import("table.zig").TableType;
|
|
1235
|
-
const TableInfoType = @import("manifest.zig").TableInfoType;
|
|
1236
|
-
const TableInfo = TableInfoType(TableType(
|
|
1237
|
-
Key,
|
|
1238
|
-
Key.Value,
|
|
1239
|
-
Key.compare_keys,
|
|
1240
|
-
Key.key_from_value,
|
|
1241
|
-
Key.sentinel_key,
|
|
1242
|
-
Key.tombstone,
|
|
1243
|
-
Key.tombstone_from_key,
|
|
1244
|
-
1, // Doesn't matter for this test.
|
|
1245
|
-
.general,
|
|
1246
|
-
));
|
|
1247
|
-
|
|
1248
|
-
const CompareInt = struct {
|
|
1249
|
-
inline fn compare_keys(a: u32, b: u32) std.math.Order {
|
|
1250
|
-
return std.math.order(a, b);
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
inline fn key_from_value(value: *const u32) u32 {
|
|
1254
|
-
return value.*;
|
|
1255
|
-
}
|
|
1256
|
-
};
|
|
1257
|
-
|
|
1258
|
-
const CompareTable = struct {
|
|
1259
|
-
inline fn compare_keys(a: u64, b: u64) std.math.Order {
|
|
1260
|
-
return std.math.order(a, b);
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
inline fn key_from_value(value: *const TableInfo) u64 {
|
|
1264
|
-
return value.address;
|
|
1265
|
-
}
|
|
1266
|
-
};
|
|
1267
|
-
|
|
1268
|
-
comptime {
|
|
1269
|
-
assert(@sizeOf(TableInfo) == 48 + @sizeOf(u128) * 2);
|
|
1270
|
-
assert(@alignOf(TableInfo) == 16);
|
|
1271
|
-
assert(@bitSizeOf(TableInfo) == @sizeOf(TableInfo) * 8);
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
const TestOptions = struct {
|
|
1275
|
-
element_type: type,
|
|
1276
|
-
node_size: u32,
|
|
1277
|
-
element_count_max: u32,
|
|
1278
|
-
};
|
|
1279
|
-
|
|
1280
|
-
var tested_padding = false;
|
|
1281
|
-
var tested_node_capacity_min = false;
|
|
1282
|
-
|
|
1283
|
-
// We want to explore not just the bottom boundary but also the surrounding area
|
|
1284
|
-
// as it may also have interesting edge cases.
|
|
1285
|
-
inline for (.{
|
|
1286
|
-
TestOptions{ .element_type = u32, .node_size = 8, .element_count_max = 3 },
|
|
1287
|
-
TestOptions{ .element_type = u32, .node_size = 8, .element_count_max = 4 },
|
|
1288
|
-
TestOptions{ .element_type = u32, .node_size = 8, .element_count_max = 5 },
|
|
1289
|
-
TestOptions{ .element_type = u32, .node_size = 8, .element_count_max = 6 },
|
|
1290
|
-
TestOptions{ .element_type = u32, .node_size = 8, .element_count_max = 1024 },
|
|
1291
|
-
TestOptions{ .element_type = u32, .node_size = 16, .element_count_max = 1024 },
|
|
1292
|
-
TestOptions{ .element_type = u32, .node_size = 32, .element_count_max = 1024 },
|
|
1293
|
-
TestOptions{ .element_type = u32, .node_size = 64, .element_count_max = 1024 },
|
|
1294
|
-
TestOptions{ .element_type = TableInfo, .node_size = 256, .element_count_max = 3 },
|
|
1295
|
-
TestOptions{ .element_type = TableInfo, .node_size = 256, .element_count_max = 4 },
|
|
1296
|
-
TestOptions{ .element_type = TableInfo, .node_size = 256, .element_count_max = 1024 },
|
|
1297
|
-
TestOptions{ .element_type = TableInfo, .node_size = 512, .element_count_max = 1024 },
|
|
1298
|
-
TestOptions{ .element_type = TableInfo, .node_size = 1024, .element_count_max = 1024 },
|
|
1299
|
-
}) |test_options| {
|
|
1300
|
-
inline for (.{ .sorted, .unsorted }) |order| {
|
|
1301
|
-
const Context = TestContext(
|
|
1302
|
-
test_options.element_type,
|
|
1303
|
-
test_options.node_size,
|
|
1304
|
-
test_options.element_count_max,
|
|
1305
|
-
if (test_options.element_type == u32) u32 else u64,
|
|
1306
|
-
if (test_options.element_type == u32) CompareInt.key_from_value else CompareTable.key_from_value,
|
|
1307
|
-
if (test_options.element_type == u32) CompareInt.compare_keys else CompareTable.compare_keys,
|
|
1308
|
-
order,
|
|
1309
|
-
options,
|
|
1310
|
-
);
|
|
1311
|
-
|
|
1312
|
-
var context = try Context.init(random);
|
|
1313
|
-
defer context.deinit();
|
|
1314
|
-
|
|
1315
|
-
try context.run();
|
|
1316
|
-
|
|
1317
|
-
if (test_options.node_size % @sizeOf(test_options.element_type) != 0) tested_padding = true;
|
|
1318
|
-
if (Context.TestArray.node_capacity == 2) tested_node_capacity_min = true;
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
assert(tested_padding);
|
|
1323
|
-
assert(tested_node_capacity_min);
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
test "SegmentedArray" {
|
|
1327
|
-
const seed = 42;
|
|
1328
|
-
try run_tests(seed, .{ .verify = true });
|
|
1329
|
-
}
|