tigerbeetle-node 0.11.12 → 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.
Files changed (143) hide show
  1. package/README.md +212 -196
  2. package/dist/bin/aarch64-linux-gnu/client.node +0 -0
  3. package/dist/bin/aarch64-linux-musl/client.node +0 -0
  4. package/dist/bin/aarch64-macos/client.node +0 -0
  5. package/dist/bin/x86_64-linux-gnu/client.node +0 -0
  6. package/dist/bin/x86_64-linux-musl/client.node +0 -0
  7. package/dist/bin/x86_64-macos/client.node +0 -0
  8. package/dist/index.js +33 -1
  9. package/dist/index.js.map +1 -1
  10. package/package-lock.json +66 -0
  11. package/package.json +8 -17
  12. package/src/index.ts +56 -1
  13. package/src/node.zig +10 -9
  14. package/dist/.client.node.sha256 +0 -1
  15. package/scripts/build_lib.sh +0 -61
  16. package/scripts/download_node_headers.sh +0 -32
  17. package/src/tigerbeetle/scripts/benchmark.bat +0 -48
  18. package/src/tigerbeetle/scripts/benchmark.sh +0 -66
  19. package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
  20. package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
  21. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
  22. package/src/tigerbeetle/scripts/install.bat +0 -7
  23. package/src/tigerbeetle/scripts/install.sh +0 -21
  24. package/src/tigerbeetle/scripts/install_zig.bat +0 -113
  25. package/src/tigerbeetle/scripts/install_zig.sh +0 -90
  26. package/src/tigerbeetle/scripts/lint.zig +0 -199
  27. package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
  28. package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -48
  29. package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
  30. package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
  31. package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
  32. package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
  33. package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
  34. package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
  35. package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
  36. package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
  37. package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
  38. package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
  39. package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
  40. package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
  41. package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
  42. package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
  43. package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
  44. package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
  45. package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
  46. package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
  47. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
  48. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
  49. package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
  50. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  51. package/src/tigerbeetle/src/benchmark.zig +0 -314
  52. package/src/tigerbeetle/src/config.zig +0 -234
  53. package/src/tigerbeetle/src/constants.zig +0 -436
  54. package/src/tigerbeetle/src/ewah.zig +0 -286
  55. package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
  56. package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
  57. package/src/tigerbeetle/src/fifo.zig +0 -120
  58. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  59. package/src/tigerbeetle/src/io/darwin.zig +0 -814
  60. package/src/tigerbeetle/src/io/linux.zig +0 -1062
  61. package/src/tigerbeetle/src/io/test.zig +0 -643
  62. package/src/tigerbeetle/src/io/windows.zig +0 -1183
  63. package/src/tigerbeetle/src/io.zig +0 -34
  64. package/src/tigerbeetle/src/iops.zig +0 -107
  65. package/src/tigerbeetle/src/lsm/README.md +0 -308
  66. package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
  67. package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
  68. package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
  69. package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
  70. package/src/tigerbeetle/src/lsm/direction.zig +0 -11
  71. package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
  72. package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
  73. package/src/tigerbeetle/src/lsm/forest.zig +0 -204
  74. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -401
  75. package/src/tigerbeetle/src/lsm/grid.zig +0 -573
  76. package/src/tigerbeetle/src/lsm/groove.zig +0 -972
  77. package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
  78. package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
  79. package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
  80. package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -877
  81. package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
  82. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
  83. package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
  84. package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
  85. package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -378
  86. package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1328
  87. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
  88. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
  89. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
  90. package/src/tigerbeetle/src/lsm/table.zig +0 -1031
  91. package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -203
  92. package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
  93. package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -220
  94. package/src/tigerbeetle/src/lsm/test.zig +0 -438
  95. package/src/tigerbeetle/src/lsm/tree.zig +0 -1193
  96. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -474
  97. package/src/tigerbeetle/src/message_bus.zig +0 -1012
  98. package/src/tigerbeetle/src/message_pool.zig +0 -156
  99. package/src/tigerbeetle/src/ring_buffer.zig +0 -399
  100. package/src/tigerbeetle/src/simulator.zig +0 -569
  101. package/src/tigerbeetle/src/state_machine/auditor.zig +0 -577
  102. package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
  103. package/src/tigerbeetle/src/state_machine.zig +0 -1881
  104. package/src/tigerbeetle/src/static_allocator.zig +0 -65
  105. package/src/tigerbeetle/src/stdx.zig +0 -162
  106. package/src/tigerbeetle/src/storage.zig +0 -393
  107. package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
  108. package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
  109. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
  110. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
  111. package/src/tigerbeetle/src/testing/cluster.zig +0 -443
  112. package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
  113. package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
  114. package/src/tigerbeetle/src/testing/id.zig +0 -99
  115. package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -364
  116. package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
  117. package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
  118. package/src/tigerbeetle/src/testing/state_machine.zig +0 -249
  119. package/src/tigerbeetle/src/testing/storage.zig +0 -757
  120. package/src/tigerbeetle/src/testing/table.zig +0 -247
  121. package/src/tigerbeetle/src/testing/time.zig +0 -84
  122. package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
  123. package/src/tigerbeetle/src/time.zig +0 -112
  124. package/src/tigerbeetle/src/tracer.zig +0 -529
  125. package/src/tigerbeetle/src/unit_tests.zig +0 -42
  126. package/src/tigerbeetle/src/vopr.zig +0 -495
  127. package/src/tigerbeetle/src/vsr/README.md +0 -209
  128. package/src/tigerbeetle/src/vsr/client.zig +0 -544
  129. package/src/tigerbeetle/src/vsr/clock.zig +0 -853
  130. package/src/tigerbeetle/src/vsr/journal.zig +0 -2413
  131. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
  132. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  133. package/src/tigerbeetle/src/vsr/replica.zig +0 -6381
  134. package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
  135. package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
  136. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
  137. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
  138. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
  139. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
  140. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
  141. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
  142. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
  143. package/src/tigerbeetle/src/vsr.zig +0 -1352
@@ -1,929 +0,0 @@
1
- const std = @import("std");
2
- const assert = std.debug.assert;
3
- const mem = std.mem;
4
-
5
- const DynamicBitSetUnmanaged = std.bit_set.DynamicBitSetUnmanaged;
6
- const MaskInt = DynamicBitSetUnmanaged.MaskInt;
7
-
8
- const constants = @import("../constants.zig");
9
-
10
- const ewah = @import("../ewah.zig").ewah(usize);
11
- const div_ceil = @import("../stdx.zig").div_ceil;
12
-
13
- /// This is logically a range of addresses within the FreeSet, but its actual fields are block
14
- /// indexes for ease of calculation.
15
- ///
16
- /// A reservation covers a range of both free and acquired blocks — when it is first created,
17
- /// it is guaranteed to cover exactly as many free blocks as were requested by `reserve()`.
18
- pub const Reservation = struct {
19
- block_base: usize,
20
- block_count: usize,
21
- /// An identifer for each reservation cycle, to verify that old reservations are not reused.
22
- session: usize,
23
- };
24
-
25
- /// The 0 address is reserved for usage as a sentinel and will never be returned by acquire().
26
- ///
27
- /// Concurrent callers must reserve free blocks before acquiring them to ensure that
28
- /// acquisition order is deterministic despite concurrent jobs acquiring blocks in
29
- /// nondeterministic order.
30
- ///
31
- /// The reservation lifecycle is:
32
- ///
33
- /// 1. Reserve: In deterministic order, each job (e.g. compaction) calls `reserve()` to
34
- /// reserve the upper bound of blocks that it may need to acquire to complete.
35
- /// 2. Acquire: The jobs run concurrently. Each job acquires blocks only from its respective
36
- /// reservation (via `acquire()`).
37
- /// 3. Forfeit: When a job finishes, it calls `forfeit()` to drop its reservation.
38
- /// 4. Done: When all pending reservations are forfeited, the reserved (but unacquired) space
39
- /// is reclaimed.
40
- ///
41
- pub const FreeSet = struct {
42
- /// If a shard has any free blocks, the corresponding index bit is zero.
43
- /// If a shard has no free blocks, the corresponding index bit is one.
44
- index: DynamicBitSetUnmanaged,
45
-
46
- /// Set bits indicate allocated blocks; unset bits indicate free blocks.
47
- blocks: DynamicBitSetUnmanaged,
48
-
49
- /// Set bits indicate blocks to be released at the next checkpoint.
50
- staging: DynamicBitSetUnmanaged,
51
-
52
- /// The number of blocks that are reserved, counting both acquired and free blocks
53
- /// from the start of `blocks`.
54
- /// Alternatively, the index of the first non-reserved block in `blocks`.
55
- reservation_blocks: usize = 0,
56
-
57
- /// The number of active reservations.
58
- reservation_count: usize = 0,
59
-
60
- /// Verify that when the caller transitions from creating reservations to forfeiting them,
61
- /// all reservations must be forfeited before additional reservations are made.
62
- reservation_state: enum {
63
- reserving,
64
- forfeiting,
65
- } = .reserving,
66
-
67
- /// Verifies that reservations are not allocated from or forefeited when they should not be.
68
- reservation_session: usize = 1,
69
-
70
- // Each shard is 8 cache lines because the CPU line fill buffer can fetch 10 lines in parallel.
71
- // And 8 is fast for division when computing the shard of a block.
72
- // Since the shard is scanned sequentially, the prefetching amortizes the cost of the single
73
- // cache miss. It also reduces the size of the index.
74
- //
75
- // e.g. 10TiB disk ÷ 64KiB/block ÷ 512*8 blocks/shard ÷ 8 shards/byte = 5120B index
76
- const shard_cache_lines = 8;
77
- pub const shard_bits = shard_cache_lines * constants.cache_line_size * @bitSizeOf(u8);
78
- comptime {
79
- assert(shard_bits == 4096);
80
- assert(@bitSizeOf(MaskInt) == 64);
81
- // Ensure there are no wasted padding bits at the end of the index.
82
- assert(shard_bits % @bitSizeOf(MaskInt) == 0);
83
- }
84
-
85
- pub fn init(allocator: mem.Allocator, blocks_count: usize) !FreeSet {
86
- assert(blocks_count % shard_bits == 0);
87
- assert(blocks_count % @bitSizeOf(usize) == 0);
88
-
89
- // Every block bit is covered by exactly one index bit.
90
- const shards_count = @divExact(blocks_count, shard_bits);
91
- var index = try DynamicBitSetUnmanaged.initEmpty(allocator, shards_count);
92
- errdefer index.deinit(allocator);
93
-
94
- var blocks = try DynamicBitSetUnmanaged.initEmpty(allocator, blocks_count);
95
- errdefer blocks.deinit(allocator);
96
-
97
- var staging = try DynamicBitSetUnmanaged.initEmpty(allocator, blocks_count);
98
- errdefer staging.deinit(allocator);
99
-
100
- assert(index.count() == 0);
101
- assert(blocks.count() == 0);
102
- assert(staging.count() == 0);
103
-
104
- return FreeSet{
105
- .index = index,
106
- .blocks = blocks,
107
- .staging = staging,
108
- };
109
- }
110
-
111
- pub fn deinit(set: *FreeSet, allocator: mem.Allocator) void {
112
- set.index.deinit(allocator);
113
- set.blocks.deinit(allocator);
114
- set.staging.deinit(allocator);
115
- }
116
-
117
- /// Returns the number of active reservations.
118
- pub fn count_reservations(set: FreeSet) usize {
119
- return set.reservation_count;
120
- }
121
-
122
- /// Returns the number of free blocks.
123
- pub fn count_free(set: FreeSet) usize {
124
- return set.blocks.capacity() - set.blocks.count();
125
- }
126
-
127
- /// Returns the number of free blocks in the reservation.
128
- pub fn count_free_reserved(set: FreeSet, reservation: Reservation) usize {
129
- assert(set.reservation_count > 0);
130
- assert(reservation.block_count > 0);
131
- assert(reservation.block_base < set.reservation_blocks);
132
- assert(reservation.block_base + reservation.block_count <= set.reservation_blocks);
133
-
134
- var count: u64 = 0;
135
- var i: u64 = 0;
136
- while (i < reservation.block_count) : (i += 1) {
137
- if (!set.blocks.isSet(reservation.block_base + i)) count += 1;
138
- }
139
- return count;
140
- }
141
-
142
- /// Returns the number of acquired blocks.
143
- pub fn count_acquired(set: FreeSet) usize {
144
- return set.blocks.count();
145
- }
146
-
147
- /// Returns the address of the highest acquired block.
148
- pub fn highest_address_acquired(set: FreeSet) ?u64 {
149
- var it = set.blocks.iterator(.{
150
- .kind = .set,
151
- .direction = .reverse,
152
- });
153
-
154
- if (it.next()) |block| {
155
- const address = block + 1;
156
- return address;
157
- } else {
158
- // All blocks are free.
159
- assert(set.blocks.count() == 0);
160
- return null;
161
- }
162
- }
163
-
164
- /// Reserve `reserve_count` free blocks. The blocks are not acquired yet.
165
- ///
166
- /// Invariants:
167
- ///
168
- /// - If a reservation is returned, it covers exactly `reserve_count` free blocks, along with
169
- /// any interleaved already-acquired blocks.
170
- /// - Active reservations are exclusive (i.e. disjoint).
171
- /// (A reservation is active until `forfeit()` is called.)
172
- ///
173
- /// Returns null if there are not enough blocks free and vacant.
174
- /// Returns a reservation which can be used with `acquire()`:
175
- /// - The caller should consider the returned Reservation as opaque and immutable.
176
- /// - Each `reserve()` call which returns a non-null Reservation must correspond to exactly one
177
- /// `forfeit()` call.
178
- pub fn reserve(set: *FreeSet, reserve_count: usize) ?Reservation {
179
- assert(set.reservation_state == .reserving);
180
- assert(reserve_count > 0);
181
-
182
- var shard_start = find_bit(
183
- set.index,
184
- @divFloor(set.reservation_blocks, shard_bits),
185
- set.index.bit_length,
186
- .unset,
187
- ) orelse return null;
188
-
189
- // The reservation may cover (and ignore) already-acquired blocks due to fragmentation.
190
- var block = std.math.max(shard_start * shard_bits, set.reservation_blocks);
191
- var reserved: usize = 0;
192
- while (reserved < reserve_count) : (reserved += 1) {
193
- block = 1 + (find_bit(
194
- set.blocks,
195
- block,
196
- set.blocks.bit_length,
197
- .unset,
198
- ) orelse return null);
199
- }
200
-
201
- const block_base = set.reservation_blocks;
202
- const block_count = block - set.reservation_blocks;
203
- set.reservation_blocks += block_count;
204
- set.reservation_count += 1;
205
-
206
- return Reservation{
207
- .block_base = block_base,
208
- .block_count = block_count,
209
- .session = set.reservation_session,
210
- };
211
- }
212
-
213
- /// After invoking `forfeit()`, the reservation must never be used again.
214
- pub fn forfeit(set: *FreeSet, reservation: Reservation) void {
215
- assert(set.reservation_session == reservation.session);
216
-
217
- set.reservation_count -= 1;
218
- if (set.reservation_count == 0) {
219
- // All reservations have been dropped.
220
- set.reservation_blocks = 0;
221
- set.reservation_session +%= 1;
222
- set.reservation_state = .reserving;
223
- } else {
224
- set.reservation_state = .forfeiting;
225
- }
226
- }
227
-
228
- /// Marks a free block from the reservation as allocated, and returns the address.
229
- /// The reservation must not have been forfeited yet.
230
- /// The reservation must belong to the current cycle of reservations.
231
- ///
232
- /// Invariants:
233
- ///
234
- /// - An acquired block cannot be acquired again until it has been released and the release
235
- /// has been checkpointed.
236
- ///
237
- /// Returns null if no free block is available in the reservation.
238
- pub fn acquire(set: *FreeSet, reservation: Reservation) ?u64 {
239
- assert(set.reservation_count > 0);
240
- assert(reservation.block_count > 0);
241
- assert(reservation.block_base < set.reservation_blocks);
242
- assert(reservation.block_base + reservation.block_count <= set.reservation_blocks);
243
- assert(reservation.session == set.reservation_session);
244
-
245
- const shard = find_bit(
246
- set.index,
247
- @divFloor(reservation.block_base, shard_bits),
248
- div_ceil(reservation.block_base + reservation.block_count, shard_bits),
249
- .unset,
250
- ) orelse return null;
251
- assert(!set.index.isSet(shard));
252
-
253
- const reservation_start = std.math.max(
254
- shard * shard_bits,
255
- reservation.block_base,
256
- );
257
- const reservation_end = reservation.block_base + reservation.block_count;
258
- const block = find_bit(
259
- set.blocks,
260
- reservation_start,
261
- reservation_end,
262
- .unset,
263
- ) orelse return null;
264
- assert(block >= reservation.block_base);
265
- assert(block <= reservation.block_base + reservation.block_count);
266
- assert(!set.blocks.isSet(block));
267
- assert(!set.staging.isSet(block));
268
-
269
- set.blocks.set(block);
270
- // Update the index when every block in the shard is allocated.
271
- if (set.find_free_block_in_shard(shard) == null) set.index.set(shard);
272
-
273
- const address = block + 1;
274
- return address;
275
- }
276
-
277
- fn find_free_block_in_shard(set: FreeSet, shard: usize) ?usize {
278
- const shard_start = shard * shard_bits;
279
- const shard_end = shard_start + shard_bits;
280
- assert(shard_start < set.blocks.bit_length);
281
-
282
- return find_bit(set.blocks, shard_start, shard_end, .unset);
283
- }
284
-
285
- pub fn is_free(set: FreeSet, address: u64) bool {
286
- const block = address - 1;
287
- return !set.blocks.isSet(block);
288
- }
289
-
290
- /// Leave the address allocated for now, but free it at the next checkpoint.
291
- /// This ensures that it will not be overwritten during the current checkpoint — the block may
292
- /// still be needed if we crash and recover from the current checkpoint.
293
- /// (TODO) If the block was created since the last checkpoint then it's safe to free immediately.
294
- /// This may reduce space amplification, especially for smaller datasets.
295
- /// (Note: This must be careful not to release while any reservations are held
296
- /// to avoid making the reservation's acquire()s nondeterministic).
297
- pub fn release(set: *FreeSet, address: u64) void {
298
- const block = address - 1;
299
- assert(set.blocks.isSet(block));
300
- assert(!set.staging.isSet(block));
301
-
302
- set.staging.set(block);
303
- }
304
-
305
- /// Given the address, marks an allocated block as free.
306
- fn release_now(set: *FreeSet, address: u64) void {
307
- const block = address - 1;
308
- assert(set.blocks.isSet(block));
309
- assert(!set.staging.isSet(block));
310
- assert(set.reservation_count == 0);
311
- assert(set.reservation_blocks == 0);
312
-
313
- set.index.unset(@divFloor(block, shard_bits));
314
- set.blocks.unset(block);
315
- }
316
-
317
- /// Free all staged blocks.
318
- /// Checkpoint must not be called while there are outstanding reservations.
319
- pub fn checkpoint(set: *FreeSet) void {
320
- assert(set.reservation_count == 0);
321
- assert(set.reservation_blocks == 0);
322
-
323
- var it = set.staging.iterator(.{ .kind = .set });
324
- while (it.next()) |block| {
325
- set.staging.unset(block);
326
- const address = block + 1;
327
- set.release_now(address);
328
- }
329
- assert(set.staging.count() == 0);
330
- }
331
-
332
- /// Temporarily marks staged blocks as free.
333
- /// Amortizes the cost of toggling staged blocks when encoding and getting the highest address.
334
- /// Does not update the index and MUST therefore be paired immediately with exclude_staging().
335
- pub fn include_staging(set: *FreeSet) void {
336
- const free = set.count_free();
337
-
338
- set.blocks.toggleSet(set.staging);
339
-
340
- // We expect the free count to increase now that staging has been included:
341
- assert(set.count_free() == free + set.staging.count());
342
- }
343
-
344
- pub fn exclude_staging(set: *FreeSet) void {
345
- const free = set.count_free();
346
-
347
- set.blocks.toggleSet(set.staging);
348
-
349
- // We expect the free count to decrease now that staging has been excluded:
350
- assert(set.count_free() == free - set.staging.count());
351
- }
352
-
353
- /// Decodes the compressed bitset in `source` into `set`.
354
- /// Panics if the `source` encoding is invalid.
355
- pub fn decode(set: *FreeSet, source: []align(@alignOf(usize)) const u8) void {
356
- // Verify that this FreeSet is entirely unallocated.
357
- assert(set.index.count() == 0);
358
- assert(set.blocks.count() == 0);
359
- assert(set.staging.count() == 0);
360
- assert(set.reservation_count == 0);
361
- assert(set.reservation_blocks == 0);
362
-
363
- const words_decoded = ewah.decode(source, bit_set_masks(set.blocks));
364
- assert(words_decoded * @bitSizeOf(MaskInt) <= set.blocks.bit_length);
365
-
366
- var shard: usize = 0;
367
- while (shard < set.index.bit_length) : (shard += 1) {
368
- if (set.find_free_block_in_shard(shard) == null) set.index.set(shard);
369
- }
370
- }
371
-
372
- /// Returns the maximum number of bytes that `blocks_count` blocks need to be encoded.
373
- pub fn encode_size_max(blocks_count: usize) usize {
374
- assert(blocks_count % shard_bits == 0);
375
- assert(blocks_count % @bitSizeOf(usize) == 0);
376
-
377
- return ewah.encode_size_max(@divExact(blocks_count, @bitSizeOf(usize)));
378
- }
379
-
380
- /// Returns the number of bytes written to `target`.
381
- /// The encoded data does *not* include staged changes.
382
- pub fn encode(set: FreeSet, target: []align(@alignOf(usize)) u8) usize {
383
- assert(target.len == FreeSet.encode_size_max(set.blocks.bit_length));
384
- assert(set.reservation_count == 0);
385
- assert(set.reservation_blocks == 0);
386
-
387
- return ewah.encode(bit_set_masks(set.blocks), target);
388
- }
389
-
390
- /// Returns `blocks_count` rounded down to the nearest multiple of shard and word bit count.
391
- /// Ensures that the result is acceptable to `FreeSet.init()`.
392
- pub fn blocks_count_floor(blocks_count: usize) usize {
393
- assert(blocks_count > 0);
394
- assert(blocks_count >= shard_bits);
395
-
396
- const floor = @divFloor(blocks_count, shard_bits) * shard_bits;
397
-
398
- // We assume that shard_bits is itself a multiple of word bit count.
399
- assert(floor % @bitSizeOf(usize) == 0);
400
-
401
- return floor;
402
- }
403
- };
404
-
405
- fn bit_set_masks(bit_set: DynamicBitSetUnmanaged) []MaskInt {
406
- const len = div_ceil(bit_set.bit_length, @bitSizeOf(MaskInt));
407
- return bit_set.masks[0..len];
408
- }
409
-
410
- test "FreeSet block shard count" {
411
- if (constants.block_size != 64 * 1024) return;
412
- const blocks_in_tb = @divExact(1 << 40, constants.block_size);
413
- try test_block_shards_count(5120 * 8, 10 * blocks_in_tb);
414
- try test_block_shards_count(5120 * 8 - 1, 10 * blocks_in_tb - FreeSet.shard_bits);
415
- try test_block_shards_count(1, FreeSet.shard_bits); // Must be at least one index bit.
416
- }
417
-
418
- fn test_block_shards_count(expect_shards_count: usize, blocks_count: usize) !void {
419
- var set = try FreeSet.init(std.testing.allocator, blocks_count);
420
- defer set.deinit(std.testing.allocator);
421
-
422
- try std.testing.expectEqual(expect_shards_count, set.index.bit_length);
423
- }
424
-
425
- test "FreeSet highest_address_acquired" {
426
- const expectEqual = std.testing.expectEqual;
427
- const blocks_count = FreeSet.shard_bits;
428
- var set = try FreeSet.init(std.testing.allocator, blocks_count);
429
- defer set.deinit(std.testing.allocator);
430
-
431
- {
432
- const reservation = set.reserve(6).?;
433
- defer set.forfeit(reservation);
434
-
435
- try expectEqual(@as(?u64, null), set.highest_address_acquired());
436
- try expectEqual(@as(?u64, 1), set.acquire(reservation));
437
- try expectEqual(@as(?u64, 2), set.acquire(reservation));
438
- try expectEqual(@as(?u64, 3), set.acquire(reservation));
439
- }
440
-
441
- try expectEqual(@as(?u64, 3), set.highest_address_acquired());
442
- set.release_now(2);
443
- try expectEqual(@as(?u64, 3), set.highest_address_acquired());
444
- set.release_now(3);
445
- try expectEqual(@as(?u64, 1), set.highest_address_acquired());
446
- set.release_now(1);
447
- try expectEqual(@as(?u64, null), set.highest_address_acquired());
448
-
449
- {
450
- const reservation = set.reserve(6).?;
451
- defer set.forfeit(reservation);
452
-
453
- try expectEqual(@as(?u64, 1), set.acquire(reservation));
454
- try expectEqual(@as(?u64, 2), set.acquire(reservation));
455
- try expectEqual(@as(?u64, 3), set.acquire(reservation));
456
- }
457
-
458
- {
459
- set.release(3);
460
- try expectEqual(@as(?u64, 3), set.highest_address_acquired());
461
-
462
- set.include_staging();
463
- try expectEqual(@as(?u64, 2), set.highest_address_acquired());
464
- set.exclude_staging();
465
-
466
- try expectEqual(@as(?u64, 3), set.highest_address_acquired());
467
- set.checkpoint();
468
- try expectEqual(@as(?u64, 2), set.highest_address_acquired());
469
- }
470
- }
471
-
472
- test "FreeSet acquire/release" {
473
- try test_acquire_release(FreeSet.shard_bits);
474
- try test_acquire_release(2 * FreeSet.shard_bits);
475
- try test_acquire_release(63 * FreeSet.shard_bits);
476
- try test_acquire_release(64 * FreeSet.shard_bits);
477
- try test_acquire_release(65 * FreeSet.shard_bits);
478
- }
479
-
480
- fn test_acquire_release(blocks_count: usize) !void {
481
- const expectEqual = std.testing.expectEqual;
482
- // Acquire everything, then release, then acquire again.
483
- var set = try FreeSet.init(std.testing.allocator, blocks_count);
484
- defer set.deinit(std.testing.allocator);
485
-
486
- var empty = try FreeSet.init(std.testing.allocator, blocks_count);
487
- defer empty.deinit(std.testing.allocator);
488
-
489
- {
490
- const reservation = set.reserve(blocks_count).?;
491
- defer set.forfeit(reservation);
492
-
493
- var i: usize = 0;
494
- while (i < blocks_count) : (i += 1) {
495
- try expectEqual(@as(?u64, i + 1), set.acquire(reservation));
496
- }
497
- try expectEqual(@as(?u64, null), set.acquire(reservation));
498
- }
499
-
500
- try expectEqual(@as(u64, set.blocks.bit_length), set.count_acquired());
501
- try expectEqual(@as(u64, 0), set.count_free());
502
-
503
- {
504
- var i: usize = 0;
505
- while (i < blocks_count) : (i += 1) set.release_now(@as(u64, i + 1));
506
- try expect_free_set_equal(empty, set);
507
- }
508
-
509
- try expectEqual(@as(u64, 0), set.count_acquired());
510
- try expectEqual(@as(u64, set.blocks.bit_length), set.count_free());
511
-
512
- {
513
- const reservation = set.reserve(blocks_count).?;
514
- defer set.forfeit(reservation);
515
-
516
- var i: usize = 0;
517
- while (i < blocks_count) : (i += 1) {
518
- try expectEqual(@as(?u64, i + 1), set.acquire(reservation));
519
- }
520
- try expectEqual(@as(?u64, null), set.acquire(reservation));
521
- }
522
- }
523
-
524
- test "FreeSet.reserve/acquire" {
525
- const blocks_count_total = 4096;
526
- var set = try FreeSet.init(std.testing.allocator, blocks_count_total);
527
- defer set.deinit(std.testing.allocator);
528
-
529
- // At most `blocks_count_total` blocks are initially available for reservation.
530
- try std.testing.expectEqual(set.reserve(blocks_count_total + 1), null);
531
- const r1 = set.reserve(blocks_count_total - 1);
532
- const r2 = set.reserve(1);
533
- try std.testing.expectEqual(set.reserve(1), null);
534
- set.forfeit(r1.?);
535
- set.forfeit(r2.?);
536
-
537
- var address: usize = 1; // Start at 1 because addresses are >0.
538
- {
539
- const reservation = set.reserve(2).?;
540
- defer set.forfeit(reservation);
541
-
542
- try std.testing.expectEqual(set.count_free_reserved(reservation), 2);
543
- try std.testing.expectEqual(set.acquire(reservation), address + 0);
544
- try std.testing.expectEqual(set.count_free_reserved(reservation), 1);
545
- try std.testing.expectEqual(set.acquire(reservation), address + 1);
546
- try std.testing.expectEqual(set.count_free_reserved(reservation), 0);
547
- try std.testing.expectEqual(set.acquire(reservation), null);
548
- }
549
- address += 2;
550
-
551
- {
552
- // Blocks are acquired from the target reservation.
553
- const reservation_1 = set.reserve(2).?;
554
- const reservation_2 = set.reserve(2).?;
555
- defer set.forfeit(reservation_1);
556
- defer set.forfeit(reservation_2);
557
-
558
- try std.testing.expectEqual(set.acquire(reservation_1), address + 0);
559
- try std.testing.expectEqual(set.acquire(reservation_2), address + 2);
560
- try std.testing.expectEqual(set.acquire(reservation_1), address + 1);
561
- try std.testing.expectEqual(set.acquire(reservation_1), null);
562
- try std.testing.expectEqual(set.acquire(reservation_2), address + 3);
563
- try std.testing.expectEqual(set.acquire(reservation_2), null);
564
- }
565
- address += 4;
566
- }
567
-
568
- test "FreeSet checkpoint" {
569
- const expectEqual = std.testing.expectEqual;
570
- const blocks_count = FreeSet.shard_bits;
571
- var set = try FreeSet.init(std.testing.allocator, blocks_count);
572
- defer set.deinit(std.testing.allocator);
573
-
574
- var empty = try FreeSet.init(std.testing.allocator, blocks_count);
575
- defer empty.deinit(std.testing.allocator);
576
-
577
- var full = try FreeSet.init(std.testing.allocator, blocks_count);
578
- defer full.deinit(std.testing.allocator);
579
-
580
- {
581
- // Acquire all of `full`'s blocks.
582
- const reservation = full.reserve(blocks_count).?;
583
- defer full.forfeit(reservation);
584
-
585
- var i: usize = 0;
586
- while (i < full.blocks.bit_length) : (i += 1) {
587
- try expectEqual(@as(?u64, i + 1), full.acquire(reservation));
588
- }
589
- }
590
-
591
- {
592
- // Acquire & stage-release every block.
593
- const reservation = set.reserve(blocks_count).?;
594
- defer set.forfeit(reservation);
595
-
596
- var i: usize = 0;
597
- while (i < set.blocks.bit_length) : (i += 1) {
598
- try expectEqual(@as(?u64, i + 1), set.acquire(reservation));
599
- set.release(i + 1);
600
-
601
- // These count functions treat staged blocks as allocated.
602
- try expectEqual(@as(u64, i + 1), set.count_acquired());
603
- try expectEqual(@as(u64, set.blocks.bit_length - i - 1), set.count_free());
604
- }
605
- // All blocks are still allocated, though staged to release at the next checkpoint.
606
- try expectEqual(@as(?u64, null), set.acquire(reservation));
607
- }
608
-
609
- // Free all the blocks.
610
- set.checkpoint();
611
- try expect_free_set_equal(empty, set);
612
- try expectEqual(@as(usize, 0), set.staging.count());
613
-
614
- // Redundant checkpointing is a noop (but safe).
615
- set.checkpoint();
616
-
617
- {
618
- // Allocate & stage-release all blocks again.
619
- const reservation = set.reserve(blocks_count).?;
620
- defer set.forfeit(reservation);
621
- var i: usize = 0;
622
- while (i < set.blocks.bit_length) : (i += 1) {
623
- try expectEqual(@as(?u64, i + 1), set.acquire(reservation));
624
- set.release(i + 1);
625
- }
626
- }
627
-
628
- var set_encoded = try std.testing.allocator.alignedAlloc(
629
- u8,
630
- @alignOf(usize),
631
- FreeSet.encode_size_max(set.blocks.bit_length),
632
- );
633
- defer std.testing.allocator.free(set_encoded);
634
-
635
- {
636
- // `encode` encodes staged blocks as free.
637
- set.include_staging();
638
- defer set.exclude_staging();
639
-
640
- const set_encoded_length = set.encode(set_encoded);
641
- var set_decoded = try FreeSet.init(std.testing.allocator, blocks_count);
642
- defer set_decoded.deinit(std.testing.allocator);
643
-
644
- set_decoded.decode(set_encoded[0..set_encoded_length]);
645
- try expect_free_set_equal(empty, set_decoded);
646
- }
647
-
648
- {
649
- // `encode` encodes staged blocks as still allocated.
650
- const set_encoded_length = set.encode(set_encoded);
651
- var set_decoded = try FreeSet.init(std.testing.allocator, blocks_count);
652
- defer set_decoded.deinit(std.testing.allocator);
653
-
654
- set_decoded.decode(set_encoded[0..set_encoded_length]);
655
- try expect_free_set_equal(full, set_decoded);
656
- }
657
- }
658
-
659
- test "FreeSet encode, decode, encode" {
660
- const shard_bits = FreeSet.shard_bits / @bitSizeOf(usize);
661
- // Uniform.
662
- try test_encode(&.{.{ .fill = .uniform_ones, .words = shard_bits }});
663
- try test_encode(&.{.{ .fill = .uniform_zeros, .words = shard_bits }});
664
- try test_encode(&.{.{ .fill = .literal, .words = shard_bits }});
665
- try test_encode(&.{.{ .fill = .uniform_ones, .words = std.math.maxInt(u16) + 1 }});
666
-
667
- // Mixed.
668
- try test_encode(&.{
669
- .{ .fill = .uniform_ones, .words = shard_bits / 4 },
670
- .{ .fill = .uniform_zeros, .words = shard_bits / 4 },
671
- .{ .fill = .literal, .words = shard_bits / 4 },
672
- .{ .fill = .uniform_ones, .words = shard_bits / 4 },
673
- });
674
-
675
- // Random.
676
- var seed: u64 = undefined;
677
- try std.os.getrandom(mem.asBytes(&seed));
678
-
679
- var prng = std.rand.DefaultPrng.init(seed);
680
- const random = prng.random();
681
-
682
- const fills = [_]TestPatternFill{ .uniform_ones, .uniform_zeros, .literal };
683
- var t: usize = 0;
684
- while (t < 10) : (t += 1) {
685
- var patterns = std.ArrayList(TestPattern).init(std.testing.allocator);
686
- defer patterns.deinit();
687
-
688
- var i: usize = 0;
689
- while (i < shard_bits) : (i += 1) {
690
- try patterns.append(.{
691
- .fill = fills[random.uintLessThan(usize, fills.len)],
692
- .words = 1,
693
- });
694
- }
695
- try test_encode(patterns.items);
696
- }
697
- }
698
-
699
- const TestPattern = struct {
700
- fill: TestPatternFill,
701
- words: usize,
702
- };
703
-
704
- const TestPatternFill = enum { uniform_ones, uniform_zeros, literal };
705
-
706
- fn test_encode(patterns: []const TestPattern) !void {
707
- var seed: u64 = undefined;
708
- try std.os.getrandom(mem.asBytes(&seed));
709
-
710
- var prng = std.rand.DefaultPrng.init(seed);
711
- const random = prng.random();
712
-
713
- var blocks_count: usize = 0;
714
- for (patterns) |pattern| blocks_count += pattern.words * @bitSizeOf(usize);
715
-
716
- var decoded_expect = try FreeSet.init(std.testing.allocator, blocks_count);
717
- defer decoded_expect.deinit(std.testing.allocator);
718
-
719
- {
720
- // The `index` will start out one-filled. Every pattern containing a zero will update the
721
- // corresponding index bit with a zero (probably multiple times) to ensure it ends up synced
722
- // with `blocks`.
723
- decoded_expect.index.toggleAll();
724
- assert(decoded_expect.index.count() == decoded_expect.index.capacity());
725
-
726
- // Fill the bitset according to the patterns.
727
- var blocks = bit_set_masks(decoded_expect.blocks);
728
- var blocks_offset: usize = 0;
729
- for (patterns) |pattern| {
730
- var i: usize = 0;
731
- while (i < pattern.words) : (i += 1) {
732
- blocks[blocks_offset] = switch (pattern.fill) {
733
- .uniform_ones => ~@as(usize, 0),
734
- .uniform_zeros => 0,
735
- .literal => random.intRangeLessThan(usize, 1, std.math.maxInt(usize)),
736
- };
737
- const index_bit = blocks_offset * @bitSizeOf(usize) / FreeSet.shard_bits;
738
- if (pattern.fill != .uniform_ones) decoded_expect.index.unset(index_bit);
739
- blocks_offset += 1;
740
- }
741
- }
742
- assert(blocks_offset == blocks.len);
743
- }
744
-
745
- var encoded = try std.testing.allocator.alignedAlloc(
746
- u8,
747
- @alignOf(usize),
748
- FreeSet.encode_size_max(decoded_expect.blocks.bit_length),
749
- );
750
- defer std.testing.allocator.free(encoded);
751
-
752
- try std.testing.expectEqual(encoded.len % 8, 0);
753
- const encoded_length = decoded_expect.encode(encoded);
754
- var decoded_actual = try FreeSet.init(std.testing.allocator, blocks_count);
755
- defer decoded_actual.deinit(std.testing.allocator);
756
-
757
- decoded_actual.decode(encoded[0..encoded_length]);
758
- try expect_free_set_equal(decoded_expect, decoded_actual);
759
- }
760
-
761
- fn expect_free_set_equal(a: FreeSet, b: FreeSet) !void {
762
- try expect_bit_set_equal(a.blocks, b.blocks);
763
- try expect_bit_set_equal(a.index, b.index);
764
- try expect_bit_set_equal(a.staging, b.staging);
765
- }
766
-
767
- fn expect_bit_set_equal(a: DynamicBitSetUnmanaged, b: DynamicBitSetUnmanaged) !void {
768
- try std.testing.expectEqual(a.bit_length, b.bit_length);
769
- const a_masks = bit_set_masks(a);
770
- const b_masks = bit_set_masks(b);
771
- for (a_masks) |aw, i| try std.testing.expectEqual(aw, b_masks[i]);
772
- }
773
-
774
- test "FreeSet decode small bitset into large bitset" {
775
- const shard_bits = FreeSet.shard_bits;
776
- var small_set = try FreeSet.init(std.testing.allocator, shard_bits);
777
- defer small_set.deinit(std.testing.allocator);
778
-
779
- {
780
- // Set up a small bitset (with blocks_count==shard_bits) with no free blocks.
781
- const reservation = small_set.reserve(small_set.blocks.bit_length).?;
782
- defer small_set.forfeit(reservation);
783
-
784
- var i: usize = 0;
785
- while (i < small_set.blocks.bit_length) : (i += 1) _ = small_set.acquire(reservation);
786
- }
787
-
788
- var small_buffer = try std.testing.allocator.alignedAlloc(
789
- u8,
790
- @alignOf(usize),
791
- FreeSet.encode_size_max(small_set.blocks.bit_length),
792
- );
793
- defer std.testing.allocator.free(small_buffer);
794
-
795
- const small_buffer_written = small_set.encode(small_buffer);
796
- // Decode the serialized small bitset into a larger bitset (with blocks_count==2*shard_bits).
797
- var big_set = try FreeSet.init(std.testing.allocator, 2 * shard_bits);
798
- defer big_set.deinit(std.testing.allocator);
799
-
800
- big_set.decode(small_buffer[0..small_buffer_written]);
801
-
802
- var block: usize = 0;
803
- while (block < 2 * shard_bits) : (block += 1) {
804
- const address = block + 1;
805
- try std.testing.expectEqual(shard_bits <= block, big_set.is_free(address));
806
- }
807
- }
808
-
809
- test "FreeSet encode/decode manual" {
810
- const encoded_expect = mem.sliceAsBytes(&[_]usize{
811
- // Mask 1: run of 2 words of 0s, then 3 literals
812
- 0 | (2 << 1) | (3 << 32),
813
- 0b10101010_10101010_10101010_10101010_10101010_10101010_10101010_10101010, // literal 1
814
- 0b01010101_01010101_01010101_01010101_01010101_01010101_01010101_01010101, // literal 2
815
- 0b10101010_10101010_10101010_10101010_10101010_10101010_10101010_10101010, // literal 3
816
- // Mask 2: run of 59 words of 1s, then 0 literals
817
- //
818
- // 59 is chosen so that because the blocks_count must be a multiple of the shard size:
819
- // shard_bits = 4096 bits = 64 words × 64 bits/word = (2+3+59)*64
820
- 1 | ((64 - 5) << 1),
821
- });
822
- const decoded_expect = [_]usize{
823
- 0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000, // run 1
824
- 0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000,
825
- 0b10101010_10101010_10101010_10101010_10101010_10101010_10101010_10101010, // literal 1
826
- 0b01010101_01010101_01010101_01010101_01010101_01010101_01010101_01010101, // literal 2
827
- 0b10101010_10101010_10101010_10101010_10101010_10101010_10101010_10101010, // literal 3
828
- } ++ ([1]usize{~@as(usize, 0)} ** (64 - 5));
829
- const blocks_count = decoded_expect.len * @bitSizeOf(usize);
830
-
831
- // Test decode.
832
- var decoded_actual = try FreeSet.init(std.testing.allocator, blocks_count);
833
- defer decoded_actual.deinit(std.testing.allocator);
834
-
835
- decoded_actual.decode(encoded_expect);
836
- try std.testing.expectEqual(decoded_expect.len, bit_set_masks(decoded_actual.blocks).len);
837
- try std.testing.expectEqualSlices(usize, &decoded_expect, bit_set_masks(decoded_actual.blocks));
838
-
839
- // Test encode.
840
- var encoded_actual = try std.testing.allocator.alignedAlloc(
841
- u8,
842
- @alignOf(usize),
843
- FreeSet.encode_size_max(decoded_actual.blocks.bit_length),
844
- );
845
- defer std.testing.allocator.free(encoded_actual);
846
-
847
- const encoded_actual_length = decoded_actual.encode(encoded_actual);
848
- try std.testing.expectEqual(encoded_expect.len, encoded_actual_length);
849
- }
850
-
851
- /// Returns the index of the first set/unset bit (relative to the start of the bitset) within
852
- /// the range bit_min…bit_max (inclusive…exclusive).
853
- fn find_bit(
854
- bit_set: DynamicBitSetUnmanaged,
855
- bit_min: usize,
856
- bit_max: usize,
857
- comptime bit_kind: std.bit_set.IteratorOptions.Type,
858
- ) ?usize {
859
- assert(bit_max >= bit_min);
860
- assert(bit_max <= bit_set.bit_length);
861
-
862
- const word_start = @divFloor(bit_min, @bitSizeOf(MaskInt)); // Inclusive.
863
- const word_offset = @mod(bit_min, @bitSizeOf(MaskInt));
864
- const word_end = div_ceil(bit_max, @bitSizeOf(MaskInt)); // Exclusive.
865
- const words_total = div_ceil(bit_set.bit_length, @bitSizeOf(MaskInt));
866
- if (word_end == word_start) return null;
867
- assert(word_end > word_start);
868
-
869
- // Only iterate over the subset of bits that were requested.
870
- var iterator = bit_set.iterator(.{ .kind = bit_kind });
871
- iterator.words_remain = bit_set.masks[word_start + 1 .. word_end];
872
-
873
- const mask = ~@as(MaskInt, 0);
874
- var word = bit_set.masks[word_start];
875
- if (bit_kind == .unset) word = ~word;
876
- iterator.bits_remain = word & std.math.shl(MaskInt, mask, word_offset);
877
-
878
- if (word_end != words_total) iterator.last_word_mask = mask;
879
-
880
- const b = bit_min - word_offset + (iterator.next() orelse return null);
881
- return if (b < bit_max) b else null;
882
- }
883
-
884
- test "find_bit" {
885
- var prng = std.rand.DefaultPrng.init(123);
886
- const random = prng.random();
887
-
888
- var bit_length: usize = 1;
889
- while (bit_length <= @bitSizeOf(std.DynamicBitSetUnmanaged.MaskInt) * 4) : (bit_length += 1) {
890
- var bit_set = try std.DynamicBitSetUnmanaged.initEmpty(std.testing.allocator, bit_length);
891
- defer bit_set.deinit(std.testing.allocator);
892
-
893
- const p = random.uintLessThan(usize, 100);
894
- var b: usize = 0;
895
- while (b < bit_length) : (b += 1) bit_set.setValue(b, p < random.uintLessThan(usize, 100));
896
-
897
- var i: usize = 0;
898
- while (i < 20) : (i += 1) try test_find_bit(random, bit_set, .set);
899
- while (i < 40) : (i += 1) try test_find_bit(random, bit_set, .unset);
900
- }
901
- }
902
-
903
- fn test_find_bit(
904
- random: std.rand.Random,
905
- bit_set: DynamicBitSetUnmanaged,
906
- comptime bit_kind: std.bit_set.IteratorOptions.Type,
907
- ) !void {
908
- const bit_min = random.uintLessThan(usize, bit_set.bit_length);
909
- const bit_max = random.uintLessThan(usize, bit_set.bit_length - bit_min) + bit_min;
910
- assert(bit_max >= bit_min);
911
- assert(bit_max <= bit_set.bit_length);
912
-
913
- const bit_actual = find_bit(bit_set, bit_min, bit_max, bit_kind);
914
- if (bit_actual) |bit| {
915
- assert(bit_set.isSet(bit) == (bit_kind == .set));
916
- assert(bit >= bit_min);
917
- assert(bit < bit_max);
918
- }
919
-
920
- var iterator = bit_set.iterator(.{ .kind = bit_kind });
921
- while (iterator.next()) |bit| {
922
- if (bit_min <= bit and bit < bit_max) {
923
- try std.testing.expectEqual(bit_actual, bit);
924
- break;
925
- }
926
- } else {
927
- try std.testing.expectEqual(bit_actual, null);
928
- }
929
- }