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,111 +0,0 @@
|
|
|
1
|
-
//! Fuzz WAL formats using different write sizes.
|
|
2
|
-
const std = @import("std");
|
|
3
|
-
const assert = std.debug.assert;
|
|
4
|
-
const log = std.log.scoped(.fuzz_journal_format);
|
|
5
|
-
|
|
6
|
-
const constants = @import("../constants.zig");
|
|
7
|
-
const stdx = @import("../stdx.zig");
|
|
8
|
-
const vsr = @import("../vsr.zig");
|
|
9
|
-
const journal = @import("./journal.zig");
|
|
10
|
-
const fuzz = @import("../testing/fuzz.zig");
|
|
11
|
-
|
|
12
|
-
pub const tigerbeetle_config = @import("../config.zig").configs.test_min;
|
|
13
|
-
|
|
14
|
-
const cluster = 0;
|
|
15
|
-
|
|
16
|
-
pub fn main() !void {
|
|
17
|
-
const allocator = std.testing.allocator;
|
|
18
|
-
const args = try fuzz.parse_fuzz_args(allocator);
|
|
19
|
-
var prng = std.rand.DefaultPrng.init(args.seed);
|
|
20
|
-
|
|
21
|
-
// +10 to occasionally test formatting into a buffer larger than the total data size.
|
|
22
|
-
const write_sectors_max = @divExact(constants.journal_size_headers, constants.sector_size);
|
|
23
|
-
const write_sectors = 1 + prng.random().uintLessThan(usize, write_sectors_max + 10);
|
|
24
|
-
const write_size = write_sectors * constants.sector_size;
|
|
25
|
-
|
|
26
|
-
log.info("write_size={} write_sectors={}", .{ write_size, write_sectors });
|
|
27
|
-
|
|
28
|
-
try fuzz_format_wal_headers(write_size);
|
|
29
|
-
try fuzz_format_wal_prepares(write_size);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
pub fn fuzz_format_wal_headers(write_size_max: usize) !void {
|
|
33
|
-
assert(write_size_max > 0);
|
|
34
|
-
assert(write_size_max % @sizeOf(vsr.Header) == 0);
|
|
35
|
-
assert(write_size_max % constants.sector_size == 0);
|
|
36
|
-
|
|
37
|
-
const write = try std.testing.allocator.alloc(u8, write_size_max);
|
|
38
|
-
defer std.testing.allocator.free(write);
|
|
39
|
-
|
|
40
|
-
var offset: usize = 0;
|
|
41
|
-
while (offset < constants.journal_size_headers) {
|
|
42
|
-
const write_size = journal.format_wal_headers(cluster, offset, write);
|
|
43
|
-
defer offset += write_size;
|
|
44
|
-
|
|
45
|
-
const write_headers = std.mem.bytesAsSlice(vsr.Header, write[0..write_size]);
|
|
46
|
-
for (write_headers) |header, i| {
|
|
47
|
-
const slot = @divExact(offset, @sizeOf(vsr.Header)) + i;
|
|
48
|
-
try verify_slot_header(slot, header);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
assert(offset == constants.journal_size_headers);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
pub fn fuzz_format_wal_prepares(write_size_max: usize) !void {
|
|
55
|
-
assert(write_size_max > 0);
|
|
56
|
-
assert(write_size_max % @sizeOf(vsr.Header) == 0);
|
|
57
|
-
assert(write_size_max % constants.sector_size == 0);
|
|
58
|
-
|
|
59
|
-
const write = try std.testing.allocator.alloc(u8, write_size_max);
|
|
60
|
-
defer std.testing.allocator.free(write);
|
|
61
|
-
|
|
62
|
-
var offset: usize = 0;
|
|
63
|
-
while (offset < constants.journal_size_prepares) {
|
|
64
|
-
const write_size = journal.format_wal_prepares(cluster, offset, write);
|
|
65
|
-
defer offset += write_size;
|
|
66
|
-
|
|
67
|
-
var offset_checked: usize = 0;
|
|
68
|
-
while (offset_checked < write_size) {
|
|
69
|
-
const offset_header_next = std.mem.alignForward(
|
|
70
|
-
offset + offset_checked,
|
|
71
|
-
constants.message_size_max,
|
|
72
|
-
) - offset;
|
|
73
|
-
|
|
74
|
-
if (offset_checked == offset_header_next) {
|
|
75
|
-
// Message header.
|
|
76
|
-
const slot = @divExact(offset + offset_checked, constants.message_size_max);
|
|
77
|
-
const header_bytes = write[offset_checked..][0..@sizeOf(vsr.Header)];
|
|
78
|
-
const header = std.mem.bytesToValue(vsr.Header, header_bytes);
|
|
79
|
-
|
|
80
|
-
try verify_slot_header(slot, header);
|
|
81
|
-
offset_checked += @sizeOf(vsr.Header);
|
|
82
|
-
} else {
|
|
83
|
-
// Message body.
|
|
84
|
-
const offset_message_end = std.math.min(offset_header_next, write_size);
|
|
85
|
-
const message_body_bytes = write[offset_checked..offset_message_end];
|
|
86
|
-
var byte: usize = 0;
|
|
87
|
-
for (std.mem.bytesAsSlice(usize, message_body_bytes)) |b| byte |= b;
|
|
88
|
-
|
|
89
|
-
try std.testing.expectEqual(byte, 0);
|
|
90
|
-
offset_checked = offset_message_end;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
assert(offset_checked == write_size);
|
|
94
|
-
}
|
|
95
|
-
assert(offset == constants.journal_size_prepares);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
fn verify_slot_header(slot: usize, header: vsr.Header) !void {
|
|
99
|
-
try std.testing.expect(header.valid_checksum());
|
|
100
|
-
try std.testing.expect(header.valid_checksum_body(&[0]u8{}));
|
|
101
|
-
try std.testing.expectEqual(header.invalid(), null);
|
|
102
|
-
try std.testing.expectEqual(header.cluster, cluster);
|
|
103
|
-
try std.testing.expectEqual(header.op, slot);
|
|
104
|
-
try std.testing.expectEqual(header.size, @sizeOf(vsr.Header));
|
|
105
|
-
if (slot == 0) {
|
|
106
|
-
try std.testing.expectEqual(header.command, .prepare);
|
|
107
|
-
try std.testing.expectEqual(header.operation, .root);
|
|
108
|
-
} else {
|
|
109
|
-
try std.testing.expectEqual(header.command, .reserved);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
const assert = std.debug.assert;
|
|
3
|
-
|
|
4
|
-
/// Marzullo's algorithm, invented by Keith Marzullo for his Ph.D. dissertation in 1984, is an
|
|
5
|
-
/// agreement algorithm used to select sources for estimating accurate time from a number of noisy
|
|
6
|
-
/// time sources. NTP uses a modified form of this called the Intersection algorithm, which returns
|
|
7
|
-
/// a larger interval for further statistical sampling. However, here we want the smallest interval.
|
|
8
|
-
pub const Marzullo = struct {
|
|
9
|
-
|
|
10
|
-
/// The smallest interval consistent with the largest number of sources.
|
|
11
|
-
pub const Interval = struct {
|
|
12
|
-
/// The lower bound on the minimum clock offset.
|
|
13
|
-
lower_bound: i64,
|
|
14
|
-
|
|
15
|
-
/// The upper bound on the maximum clock offset.
|
|
16
|
-
upper_bound: i64,
|
|
17
|
-
|
|
18
|
-
/// The number of "true chimers" consistent with the largest number of sources.
|
|
19
|
-
sources_true: u8,
|
|
20
|
-
|
|
21
|
-
/// The number of "false chimers" falling outside this interval.
|
|
22
|
-
/// Where `sources_false` plus `sources_true` always equals the total number of sources.
|
|
23
|
-
sources_false: u8,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/// A tuple represents either the lower or upper end of a bound, and is fed as input to the
|
|
27
|
-
/// Marzullo algorithm to compute the smallest interval across all tuples.
|
|
28
|
-
/// For example, given a clock offset to a remote replica of 3s, a round trip time of 1s, and
|
|
29
|
-
/// a maximum tolerance between clocks of 100ms on either side, we might create two tuples, the
|
|
30
|
-
/// lower bound having an offset of 2.4s and the upper bound having an offset of 3.6s,
|
|
31
|
-
/// to represent the error introduced by the round trip time and by the clocks themselves.
|
|
32
|
-
pub const Tuple = struct {
|
|
33
|
-
/// An identifier, the index of the clock source in the list of clock sources:
|
|
34
|
-
source: u8,
|
|
35
|
-
offset: i64,
|
|
36
|
-
bound: enum {
|
|
37
|
-
lower,
|
|
38
|
-
upper,
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/// Returns the smallest interval consistent with the largest number of sources.
|
|
43
|
-
pub fn smallest_interval(tuples: []Tuple) Interval {
|
|
44
|
-
// There are two bounds (lower and upper) per source clock offset sample.
|
|
45
|
-
const sources = @intCast(u8, @divExact(tuples.len, 2));
|
|
46
|
-
|
|
47
|
-
if (sources == 0) {
|
|
48
|
-
return Interval{
|
|
49
|
-
.lower_bound = 0,
|
|
50
|
-
.upper_bound = 0,
|
|
51
|
-
.sources_true = 0,
|
|
52
|
-
.sources_false = 0,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Use a simpler sort implementation than the complexity of `std.sort.sort()` for safety:
|
|
57
|
-
std.sort.insertionSort(Tuple, tuples, {}, less_than);
|
|
58
|
-
|
|
59
|
-
// Here is a description of the algorithm:
|
|
60
|
-
// https://en.wikipedia.org/wiki/Marzullo%27s_algorithm#Method
|
|
61
|
-
var best: i64 = 0;
|
|
62
|
-
var count: i64 = 0;
|
|
63
|
-
var previous: ?Tuple = null;
|
|
64
|
-
var interval: Interval = undefined;
|
|
65
|
-
|
|
66
|
-
for (tuples) |tuple, i| {
|
|
67
|
-
// Verify that our sort implementation is correct:
|
|
68
|
-
if (previous) |p| {
|
|
69
|
-
assert(p.offset <= tuple.offset);
|
|
70
|
-
if (p.offset == tuple.offset) {
|
|
71
|
-
if (p.bound != tuple.bound) {
|
|
72
|
-
assert(p.bound == .lower and tuple.bound == .upper);
|
|
73
|
-
} else {
|
|
74
|
-
assert(p.source < tuple.source);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
previous = tuple;
|
|
79
|
-
|
|
80
|
-
// Update the current number of overlapping intervals:
|
|
81
|
-
switch (tuple.bound) {
|
|
82
|
-
.lower => count += 1,
|
|
83
|
-
.upper => count -= 1,
|
|
84
|
-
}
|
|
85
|
-
// The last upper bound tuple will have a count of one less than the lower bound.
|
|
86
|
-
// Therefore, we should never see count >= best for the last tuple:
|
|
87
|
-
if (count > best) {
|
|
88
|
-
best = count;
|
|
89
|
-
interval.lower_bound = tuple.offset;
|
|
90
|
-
interval.upper_bound = tuples[i + 1].offset;
|
|
91
|
-
} else if (count == best and tuples[i + 1].bound == .upper) {
|
|
92
|
-
// This is a tie for best overlap. Both intervals have the same number of sources.
|
|
93
|
-
// We want to choose the smaller of the two intervals:
|
|
94
|
-
const alternative = tuples[i + 1].offset - tuple.offset;
|
|
95
|
-
if (alternative < interval.upper_bound - interval.lower_bound) {
|
|
96
|
-
interval.lower_bound = tuple.offset;
|
|
97
|
-
interval.upper_bound = tuples[i + 1].offset;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
assert(previous.?.bound == .upper);
|
|
102
|
-
|
|
103
|
-
// The number of false sources (ones which do not overlap the optimal interval) is the
|
|
104
|
-
// number of sources minus the value of `best`:
|
|
105
|
-
assert(best <= sources);
|
|
106
|
-
interval.sources_true = @intCast(u8, best);
|
|
107
|
-
interval.sources_false = @intCast(u8, sources - @intCast(u8, best));
|
|
108
|
-
assert(interval.sources_true + interval.sources_false == sources);
|
|
109
|
-
|
|
110
|
-
return interval;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/// Sorts the list of tuples by clock offset. If two tuples with the same offset but opposite
|
|
114
|
-
/// bounds exist, indicating that one interval ends just as another begins, then a method of
|
|
115
|
-
/// deciding which comes first is necessary. Such an occurrence can be considered an overlap
|
|
116
|
-
/// with no duration, which can be found by the algorithm by sorting the lower bound before the
|
|
117
|
-
/// upper bound. Alternatively, if such pathological overlaps are considered objectionable then
|
|
118
|
-
/// they can be avoided by sorting the upper bound before the lower bound.
|
|
119
|
-
fn less_than(context: void, a: Tuple, b: Tuple) bool {
|
|
120
|
-
_ = context;
|
|
121
|
-
|
|
122
|
-
if (a.offset < b.offset) return true;
|
|
123
|
-
if (b.offset < a.offset) return false;
|
|
124
|
-
if (a.bound == .lower and b.bound == .upper) return true;
|
|
125
|
-
if (b.bound == .lower and a.bound == .upper) return false;
|
|
126
|
-
// Use the source index to break the tie and ensure the sort is fully specified and stable
|
|
127
|
-
// so that different sort algorithms sort the same way:
|
|
128
|
-
if (a.source < b.source) return true;
|
|
129
|
-
if (b.source < a.source) return false;
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
fn test_smallest_interval(bounds: []const i64, smallest_interval: Marzullo.Interval) !void {
|
|
135
|
-
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
136
|
-
defer arena.deinit();
|
|
137
|
-
|
|
138
|
-
const allocator = arena.allocator();
|
|
139
|
-
|
|
140
|
-
var tuples = try allocator.alloc(Marzullo.Tuple, bounds.len);
|
|
141
|
-
for (bounds) |bound, i| {
|
|
142
|
-
tuples[i] = .{
|
|
143
|
-
.source = @intCast(u8, @divTrunc(i, 2)),
|
|
144
|
-
.offset = bound,
|
|
145
|
-
.bound = if (i % 2 == 0) .lower else .upper,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
var interval = Marzullo.smallest_interval(tuples);
|
|
150
|
-
try std.testing.expectEqual(smallest_interval, interval);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
test {
|
|
154
|
-
try test_smallest_interval(
|
|
155
|
-
&[_]i64{
|
|
156
|
-
11, 13,
|
|
157
|
-
10, 12,
|
|
158
|
-
8, 12,
|
|
159
|
-
},
|
|
160
|
-
Marzullo.Interval{
|
|
161
|
-
.lower_bound = 11,
|
|
162
|
-
.upper_bound = 12,
|
|
163
|
-
.sources_true = 3,
|
|
164
|
-
.sources_false = 0,
|
|
165
|
-
},
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
try test_smallest_interval(
|
|
169
|
-
&[_]i64{
|
|
170
|
-
8, 12,
|
|
171
|
-
11, 13,
|
|
172
|
-
14, 15,
|
|
173
|
-
},
|
|
174
|
-
Marzullo.Interval{
|
|
175
|
-
.lower_bound = 11,
|
|
176
|
-
.upper_bound = 12,
|
|
177
|
-
.sources_true = 2,
|
|
178
|
-
.sources_false = 1,
|
|
179
|
-
},
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
try test_smallest_interval(
|
|
183
|
-
&[_]i64{
|
|
184
|
-
-10, 10,
|
|
185
|
-
-1, 1,
|
|
186
|
-
0, 0,
|
|
187
|
-
},
|
|
188
|
-
Marzullo.Interval{
|
|
189
|
-
.lower_bound = 0,
|
|
190
|
-
.upper_bound = 0,
|
|
191
|
-
.sources_true = 3,
|
|
192
|
-
.sources_false = 0,
|
|
193
|
-
},
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
// The upper bound of the first interval overlaps inclusively with the lower of the last.
|
|
197
|
-
try test_smallest_interval(
|
|
198
|
-
&[_]i64{
|
|
199
|
-
8, 12,
|
|
200
|
-
10, 11,
|
|
201
|
-
8, 10,
|
|
202
|
-
},
|
|
203
|
-
Marzullo.Interval{
|
|
204
|
-
.lower_bound = 10,
|
|
205
|
-
.upper_bound = 10,
|
|
206
|
-
.sources_true = 3,
|
|
207
|
-
.sources_false = 0,
|
|
208
|
-
},
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
// The first smallest interval is selected. The alternative with equal overlap is 10..12.
|
|
212
|
-
// However, while this shares the same number of sources, it is not the smallest interval.
|
|
213
|
-
try test_smallest_interval(
|
|
214
|
-
&[_]i64{
|
|
215
|
-
8, 12,
|
|
216
|
-
10, 12,
|
|
217
|
-
8, 9,
|
|
218
|
-
},
|
|
219
|
-
Marzullo.Interval{
|
|
220
|
-
.lower_bound = 8,
|
|
221
|
-
.upper_bound = 9,
|
|
222
|
-
.sources_true = 2,
|
|
223
|
-
.sources_false = 1,
|
|
224
|
-
},
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
// The last smallest interval is selected. The alternative with equal overlap is 7..9.
|
|
228
|
-
// However, while this shares the same number of sources, it is not the smallest interval.
|
|
229
|
-
try test_smallest_interval(
|
|
230
|
-
&[_]i64{
|
|
231
|
-
7, 9,
|
|
232
|
-
7, 12,
|
|
233
|
-
10, 11,
|
|
234
|
-
},
|
|
235
|
-
Marzullo.Interval{
|
|
236
|
-
.lower_bound = 10,
|
|
237
|
-
.upper_bound = 11,
|
|
238
|
-
.sources_true = 2,
|
|
239
|
-
.sources_false = 1,
|
|
240
|
-
},
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
// The same idea as the previous test, but with negative offsets.
|
|
244
|
-
try test_smallest_interval(
|
|
245
|
-
&[_]i64{
|
|
246
|
-
-9, -7,
|
|
247
|
-
-12, -7,
|
|
248
|
-
-11, -10,
|
|
249
|
-
},
|
|
250
|
-
Marzullo.Interval{
|
|
251
|
-
.lower_bound = -11,
|
|
252
|
-
.upper_bound = -10,
|
|
253
|
-
.sources_true = 2,
|
|
254
|
-
.sources_false = 1,
|
|
255
|
-
},
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
// A cluster of one with no remote sources.
|
|
259
|
-
try test_smallest_interval(
|
|
260
|
-
&[_]i64{},
|
|
261
|
-
Marzullo.Interval{
|
|
262
|
-
.lower_bound = 0,
|
|
263
|
-
.upper_bound = 0,
|
|
264
|
-
.sources_true = 0,
|
|
265
|
-
.sources_false = 0,
|
|
266
|
-
},
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
// A cluster of two with one remote source.
|
|
270
|
-
try test_smallest_interval(
|
|
271
|
-
&[_]i64{
|
|
272
|
-
1, 3,
|
|
273
|
-
},
|
|
274
|
-
Marzullo.Interval{
|
|
275
|
-
.lower_bound = 1,
|
|
276
|
-
.upper_bound = 3,
|
|
277
|
-
.sources_true = 1,
|
|
278
|
-
.sources_false = 0,
|
|
279
|
-
},
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
// A cluster of three with agreement.
|
|
283
|
-
try test_smallest_interval(
|
|
284
|
-
&[_]i64{
|
|
285
|
-
1, 3,
|
|
286
|
-
2, 2,
|
|
287
|
-
},
|
|
288
|
-
Marzullo.Interval{
|
|
289
|
-
.lower_bound = 2,
|
|
290
|
-
.upper_bound = 2,
|
|
291
|
-
.sources_true = 2,
|
|
292
|
-
.sources_false = 0,
|
|
293
|
-
},
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
// A cluster of three with no agreement, still returns the smallest interval.
|
|
297
|
-
try test_smallest_interval(
|
|
298
|
-
&[_]i64{
|
|
299
|
-
1, 3,
|
|
300
|
-
4, 5,
|
|
301
|
-
},
|
|
302
|
-
Marzullo.Interval{
|
|
303
|
-
.lower_bound = 4,
|
|
304
|
-
.upper_bound = 5,
|
|
305
|
-
.sources_true = 1,
|
|
306
|
-
.sources_false = 1,
|
|
307
|
-
},
|
|
308
|
-
);
|
|
309
|
-
}
|