tigerbeetle-node 0.11.8 → 0.11.10

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 (85) hide show
  1. package/dist/.client.node.sha256 +1 -1
  2. package/package.json +4 -3
  3. package/scripts/build_lib.sh +41 -0
  4. package/src/node.zig +1 -1
  5. package/src/tigerbeetle/scripts/validate_docs.sh +7 -1
  6. package/src/tigerbeetle/src/benchmark.zig +3 -3
  7. package/src/tigerbeetle/src/config.zig +31 -16
  8. package/src/tigerbeetle/src/constants.zig +48 -9
  9. package/src/tigerbeetle/src/ewah.zig +5 -5
  10. package/src/tigerbeetle/src/ewah_fuzz.zig +1 -1
  11. package/src/tigerbeetle/src/lsm/binary_search.zig +1 -1
  12. package/src/tigerbeetle/src/lsm/bloom_filter.zig +1 -1
  13. package/src/tigerbeetle/src/lsm/compaction.zig +34 -21
  14. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +84 -104
  15. package/src/tigerbeetle/src/lsm/grid.zig +19 -13
  16. package/src/tigerbeetle/src/lsm/manifest_log.zig +8 -10
  17. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +18 -13
  18. package/src/tigerbeetle/src/lsm/merge_iterator.zig +1 -1
  19. package/src/tigerbeetle/src/lsm/segmented_array.zig +17 -17
  20. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +1 -1
  21. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +1 -1
  22. package/src/tigerbeetle/src/lsm/table.zig +8 -20
  23. package/src/tigerbeetle/src/lsm/table_immutable.zig +1 -1
  24. package/src/tigerbeetle/src/lsm/table_iterator.zig +3 -3
  25. package/src/tigerbeetle/src/lsm/table_mutable.zig +14 -2
  26. package/src/tigerbeetle/src/lsm/test.zig +5 -4
  27. package/src/tigerbeetle/src/lsm/tree.zig +1 -2
  28. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +85 -115
  29. package/src/tigerbeetle/src/message_bus.zig +4 -4
  30. package/src/tigerbeetle/src/message_pool.zig +7 -10
  31. package/src/tigerbeetle/src/ring_buffer.zig +22 -12
  32. package/src/tigerbeetle/src/simulator.zig +366 -239
  33. package/src/tigerbeetle/src/state_machine/auditor.zig +5 -5
  34. package/src/tigerbeetle/src/state_machine/workload.zig +3 -3
  35. package/src/tigerbeetle/src/state_machine.zig +190 -178
  36. package/src/tigerbeetle/src/{util.zig → stdx.zig} +2 -0
  37. package/src/tigerbeetle/src/storage.zig +13 -6
  38. package/src/tigerbeetle/src/{test → testing/cluster}/message_bus.zig +3 -3
  39. package/src/tigerbeetle/src/{test → testing/cluster}/network.zig +46 -22
  40. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +169 -0
  41. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +202 -0
  42. package/src/tigerbeetle/src/testing/cluster.zig +443 -0
  43. package/src/tigerbeetle/src/{test → testing}/fuzz.zig +0 -0
  44. package/src/tigerbeetle/src/testing/hash_log.zig +66 -0
  45. package/src/tigerbeetle/src/{test → testing}/id.zig +0 -0
  46. package/src/tigerbeetle/src/testing/packet_simulator.zig +365 -0
  47. package/src/tigerbeetle/src/{test → testing}/priority_queue.zig +1 -1
  48. package/src/tigerbeetle/src/testing/reply_sequence.zig +139 -0
  49. package/src/tigerbeetle/src/{test → testing}/state_machine.zig +3 -1
  50. package/src/tigerbeetle/src/testing/storage.zig +757 -0
  51. package/src/tigerbeetle/src/{test → testing}/table.zig +21 -0
  52. package/src/tigerbeetle/src/{test → testing}/time.zig +0 -0
  53. package/src/tigerbeetle/src/tigerbeetle.zig +2 -0
  54. package/src/tigerbeetle/src/tracer.zig +3 -3
  55. package/src/tigerbeetle/src/unit_tests.zig +4 -4
  56. package/src/tigerbeetle/src/vopr.zig +2 -2
  57. package/src/tigerbeetle/src/vsr/client.zig +5 -2
  58. package/src/tigerbeetle/src/vsr/clock.zig +93 -53
  59. package/src/tigerbeetle/src/vsr/journal.zig +109 -98
  60. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +2 -2
  61. package/src/tigerbeetle/src/vsr/replica.zig +1983 -1430
  62. package/src/tigerbeetle/src/vsr/replica_format.zig +13 -13
  63. package/src/tigerbeetle/src/vsr/superblock.zig +240 -142
  64. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +7 -7
  65. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +1 -1
  66. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +1 -1
  67. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +49 -14
  68. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +38 -19
  69. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +48 -48
  70. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +51 -51
  71. package/src/tigerbeetle/src/vsr.zig +99 -33
  72. package/src/tigerbeetle/src/demo.zig +0 -132
  73. package/src/tigerbeetle/src/demo_01_create_accounts.zig +0 -35
  74. package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +0 -7
  75. package/src/tigerbeetle/src/demo_03_create_transfers.zig +0 -37
  76. package/src/tigerbeetle/src/demo_04_create_pending_transfers.zig +0 -61
  77. package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +0 -37
  78. package/src/tigerbeetle/src/demo_06_void_pending_transfers.zig +0 -24
  79. package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +0 -7
  80. package/src/tigerbeetle/src/test/cluster.zig +0 -352
  81. package/src/tigerbeetle/src/test/conductor.zig +0 -366
  82. package/src/tigerbeetle/src/test/packet_simulator.zig +0 -398
  83. package/src/tigerbeetle/src/test/state_checker.zig +0 -169
  84. package/src/tigerbeetle/src/test/storage.zig +0 -864
  85. package/src/tigerbeetle/src/test/storage_checker.zig +0 -204
@@ -3,7 +3,7 @@
3
3
  //! * vsr_state
4
4
  //! - vsr_state.commit_min is initially 0 (for a newly-formatted replica).
5
5
  //! - vsr_state.commit_min ≤ vsr_state.commit_max
6
- //! - vsr_state.view_normal ≤ vsr_state.view
6
+ //! - vsr_state.log_view ≤ vsr_state.view
7
7
  //! - checkpoint() must advance the superblock's vsr_state.commit_min.
8
8
  //! - view_change() must not advance the superblock's vsr_state.commit_min.
9
9
  //! - All fields of vsr_state except commit_min_checksum are monotonically increasing over
@@ -17,7 +17,7 @@ const meta = std.meta;
17
17
  const os = std.os;
18
18
 
19
19
  const constants = @import("../constants.zig");
20
- const div_ceil = @import("../util.zig").div_ceil;
20
+ const stdx = @import("../stdx.zig");
21
21
  const vsr = @import("../vsr.zig");
22
22
  const log = std.log.scoped(.superblock);
23
23
 
@@ -32,8 +32,11 @@ pub const Quorums = @import("superblock_quorums.zig").QuorumsType(.{
32
32
 
33
33
  pub const SuperBlockVersion: u16 = 0;
34
34
 
35
+ const vsr_headers_reserved_size = constants.sector_size -
36
+ ((constants.view_change_headers_max * @sizeOf(vsr.Header)) % constants.sector_size);
37
+
35
38
  // Fields are aligned to work as an extern or packed struct.
36
- pub const SuperBlockSector = extern struct {
39
+ pub const SuperBlockHeader = extern struct {
37
40
  checksum: u128 = undefined,
38
41
 
39
42
  /// Protects against misdirected reads at startup.
@@ -97,7 +100,19 @@ pub const SuperBlockSector = extern struct {
97
100
  /// The size of the client table entries stored in the superblock trailer.
98
101
  client_table_size: u32,
99
102
 
100
- reserved: [3148]u8 = [_]u8{0} ** 3148,
103
+ /// The number of headers in vsr_headers_all.
104
+ vsr_headers_count: u32,
105
+
106
+ reserved: [3144]u8 = [_]u8{0} ** 3144,
107
+
108
+ /// SV/DVC header suffix. Headers are ordered from high-to-low op.
109
+ /// Unoccupied headers (after vsr_headers_count) are zeroed.
110
+ ///
111
+ /// When `vsr_state.log_view < vsr_state.view`, the headers are for a DVC.
112
+ /// When `vsr_state.log_view = vsr_state.view`, the headers are for a SV.
113
+ vsr_headers_all: [constants.view_change_headers_max]vsr.Header,
114
+ vsr_headers_reserved: [vsr_headers_reserved_size]u8 =
115
+ [_]u8{0} ** vsr_headers_reserved_size,
101
116
 
102
117
  pub const VSRState = extern struct {
103
118
  /// The vsr.Header.checksum of commit_min's message.
@@ -110,7 +125,7 @@ pub const SuperBlockSector = extern struct {
110
125
  commit_max: u64,
111
126
 
112
127
  /// The last view in which the replica's status was normal.
113
- view_normal: u32,
128
+ log_view: u32,
114
129
 
115
130
  /// The view number of the replica.
116
131
  view: u32,
@@ -128,25 +143,25 @@ pub const SuperBlockSector = extern struct {
128
143
  .commit_min_checksum = vsr.Header.root_prepare(cluster).checksum,
129
144
  .commit_min = 0,
130
145
  .commit_max = 0,
131
- .view_normal = 0,
146
+ .log_view = 0,
132
147
  .view = 0,
133
148
  };
134
149
  }
135
150
 
136
151
  pub fn internally_consistent(state: VSRState) bool {
137
- return state.commit_max >= state.commit_min and state.view >= state.view_normal;
152
+ return state.commit_max >= state.commit_min and state.view >= state.log_view;
138
153
  }
139
154
 
140
155
  pub fn monotonic(old: VSRState, new: VSRState) bool {
141
156
  assert(old.internally_consistent());
142
157
  assert(new.internally_consistent());
143
- // The last case is for when checking monotonic() from the sequence=0 sector.
158
+ // The last case is for when checking monotonic() from the sequence=0 header.
144
159
  assert(old.commit_min != new.commit_min or
145
160
  old.commit_min_checksum == new.commit_min_checksum or
146
161
  (old.commit_min_checksum == 0 and old.commit_min == 0));
147
162
 
148
163
  if (old.view > new.view) return false;
149
- if (old.view_normal > new.view_normal) return false;
164
+ if (old.log_view > new.log_view) return false;
150
165
  if (old.commit_min > new.commit_min) return false;
151
166
  if (old.commit_max > new.commit_max) return false;
152
167
 
@@ -159,11 +174,6 @@ pub const SuperBlockSector = extern struct {
159
174
  return !meta.eql(old, new);
160
175
  }
161
176
 
162
- pub fn update(state: *VSRState, new: VSRState) void {
163
- assert(state.would_be_updated_by(new));
164
- state.* = new;
165
- }
166
-
167
177
  /// Compaction is one bar ahead of superblock's commit_min.
168
178
  /// The commits from the bar following commit_min were in the mutable table, and
169
179
  /// thus not preserved in the checkpoint.
@@ -205,14 +215,16 @@ pub const SuperBlockSector = extern struct {
205
215
  };
206
216
 
207
217
  comptime {
208
- assert(@sizeOf(SuperBlockSector) == constants.sector_size);
218
+ assert(@sizeOf(SuperBlockHeader) % constants.sector_size == 0);
219
+ assert(@divExact(@sizeOf(SuperBlockHeader), constants.sector_size) >= 2);
220
+ assert(@offsetOf(SuperBlockHeader, "vsr_headers_all") == constants.sector_size);
209
221
  // Assert that there is no implicit padding in the struct.
210
- assert(@bitSizeOf(SuperBlockSector) == @sizeOf(SuperBlockSector) * 8);
222
+ assert(@bitSizeOf(SuperBlockHeader) == @sizeOf(SuperBlockHeader) * 8);
211
223
  }
212
224
 
213
- pub fn calculate_checksum(superblock: *const SuperBlockSector) u128 {
214
- comptime assert(meta.fieldIndex(SuperBlockSector, "checksum") == 0);
215
- comptime assert(meta.fieldIndex(SuperBlockSector, "copy") == 1);
225
+ pub fn calculate_checksum(superblock: *const SuperBlockHeader) u128 {
226
+ comptime assert(meta.fieldIndex(SuperBlockHeader, "checksum") == 0);
227
+ comptime assert(meta.fieldIndex(SuperBlockHeader, "copy") == 1);
216
228
 
217
229
  const checksum_size = @sizeOf(@TypeOf(superblock.checksum));
218
230
  comptime assert(checksum_size == @sizeOf(u128));
@@ -225,24 +237,33 @@ pub const SuperBlockSector = extern struct {
225
237
  return vsr.checksum(std.mem.asBytes(superblock)[ignore_size..]);
226
238
  }
227
239
 
228
- pub fn set_checksum(superblock: *SuperBlockSector) void {
240
+ pub fn set_checksum(superblock: *SuperBlockHeader) void {
229
241
  assert(superblock.copy < constants.superblock_copies);
230
242
  assert(superblock.version == SuperBlockVersion);
231
243
  assert(superblock.flags == 0);
232
244
 
233
- assert(@bitCast(u32, superblock.reserved[0..4].*) == 0);
234
- for (mem.bytesAsSlice(u64, superblock.reserved[4..])) |word| assert(word == 0);
245
+ for (mem.bytesAsSlice(u64, &superblock.reserved)) |word| assert(word == 0);
235
246
  for (mem.bytesAsSlice(u64, &superblock.vsr_state.reserved)) |word| assert(word == 0);
247
+ for (mem.bytesAsSlice(u64, &superblock.vsr_headers_reserved)) |word| assert(word == 0);
236
248
 
237
249
  superblock.checksum = superblock.calculate_checksum();
238
250
  }
239
251
 
240
- pub fn valid_checksum(superblock: *const SuperBlockSector) bool {
252
+ pub fn valid_checksum(superblock: *const SuperBlockHeader) bool {
241
253
  return superblock.checksum == superblock.calculate_checksum();
242
254
  }
243
255
 
244
256
  /// Does not consider { checksum, copy } when comparing equality.
245
- pub fn equal(a: *const SuperBlockSector, b: *const SuperBlockSector) bool {
257
+ pub fn equal(a: *const SuperBlockHeader, b: *const SuperBlockHeader) bool {
258
+ for (mem.bytesAsSlice(u64, &a.reserved)) |word| assert(word == 0);
259
+ for (mem.bytesAsSlice(u64, &b.reserved)) |word| assert(word == 0);
260
+
261
+ for (mem.bytesAsSlice(u64, &a.vsr_state.reserved)) |word| assert(word == 0);
262
+ for (mem.bytesAsSlice(u64, &b.vsr_state.reserved)) |word| assert(word == 0);
263
+
264
+ for (mem.bytesAsSlice(u64, &a.vsr_headers_reserved)) |word| assert(word == 0);
265
+ for (mem.bytesAsSlice(u64, &b.vsr_headers_reserved)) |word| assert(word == 0);
266
+
246
267
  if (a.version != b.version) return false;
247
268
  if (a.replica != b.replica) return false;
248
269
  if (a.cluster != b.cluster) return false;
@@ -258,17 +279,17 @@ pub const SuperBlockSector = extern struct {
258
279
  if (!meta.eql(a.snapshots, b.snapshots)) return false;
259
280
  if (a.manifest_size != b.manifest_size) return false;
260
281
  if (a.free_set_size != b.free_set_size) return false;
261
-
262
- assert(@bitCast(u32, a.reserved[0..4].*) == 0);
263
- assert(@bitCast(u32, b.reserved[0..4].*) == 0);
264
- for (mem.bytesAsSlice(u64, a.reserved[4..])) |word| assert(word == 0);
265
- for (mem.bytesAsSlice(u64, b.reserved[4..])) |word| assert(word == 0);
266
-
267
- for (mem.bytesAsSlice(u64, &a.vsr_state.reserved)) |word| assert(word == 0);
268
- for (mem.bytesAsSlice(u64, &b.vsr_state.reserved)) |word| assert(word == 0);
282
+ if (a.vsr_headers_count != b.vsr_headers_count) return false;
283
+ if (!meta.eql(a.vsr_headers_all, b.vsr_headers_all)) return false;
269
284
 
270
285
  return true;
271
286
  }
287
+
288
+ pub fn vsr_headers(superblock: *const SuperBlockHeader) vsr.ViewChangeHeaders {
289
+ return vsr.ViewChangeHeaders.init(
290
+ superblock.vsr_headers_all[0..superblock.vsr_headers_count],
291
+ );
292
+ }
272
293
  };
273
294
 
274
295
  comptime {
@@ -282,12 +303,12 @@ comptime {
282
303
  pub const superblock_zone_size = superblock_copy_size * constants.superblock_copies;
283
304
 
284
305
  /// The size of an individual superblock including trailer.
285
- pub const superblock_copy_size = @sizeOf(SuperBlockSector) + superblock_trailer_size_max;
306
+ pub const superblock_copy_size = @sizeOf(SuperBlockHeader) + superblock_trailer_size_max;
286
307
  comptime {
287
308
  assert(superblock_copy_size % constants.sector_size == 0);
288
309
  }
289
310
 
290
- /// The maximum possible size of the superblock trailer, following the superblock sector.
311
+ /// The maximum possible size of the superblock trailer, following the superblock header.
291
312
  pub const superblock_trailer_size_max = blk: {
292
313
  // To calculate the size of the superblock trailer we need to know:
293
314
  // 1. the maximum number of manifest blocks that should be able to be referenced, and
@@ -312,7 +333,7 @@ pub const superblock_trailer_size_max = blk: {
312
333
 
313
334
  // A manifest block reference of 40 bytes contains a tree hash, checksum, and address.
314
335
  // These references are stored in struct-of-arrays layout in the trailer for the sake of alignment.
315
- pub const superblock_trailer_manifest_size_max = blk: {
336
+ const superblock_trailer_manifest_size_max = blk: {
316
337
  assert(SuperBlockManifest.BlockReferenceSize == 16 + 16 + 8);
317
338
 
318
339
  // Use a multiple of sector * reference so that the size is exactly divisible without padding:
@@ -321,14 +342,14 @@ pub const superblock_trailer_manifest_size_max = blk: {
321
342
  break :blk 16 * constants.sector_size * SuperBlockManifest.BlockReferenceSize;
322
343
  };
323
344
 
324
- pub const superblock_trailer_free_set_size_max = blk: {
345
+ const superblock_trailer_free_set_size_max = blk: {
325
346
  const encode_size_max = SuperBlockFreeSet.encode_size_max(block_count_max);
326
347
  assert(encode_size_max > 0);
327
348
 
328
349
  break :blk vsr.sector_ceil(encode_size_max);
329
350
  };
330
351
 
331
- pub const superblock_trailer_client_table_size_max = blk: {
352
+ const superblock_trailer_client_table_size_max = blk: {
332
353
  const encode_size_max = SuperBlockClientTable.encode_size_max;
333
354
  assert(encode_size_max > 0);
334
355
 
@@ -342,7 +363,7 @@ pub const data_file_size_min = blk: {
342
363
  /// The maximum number of blocks in the grid.
343
364
  const block_count_max = blk: {
344
365
  var size = constants.storage_size_max;
345
- size -= constants.superblock_copies * @sizeOf(SuperBlockSector);
366
+ size -= constants.superblock_copies * @sizeOf(SuperBlockHeader);
346
367
  size -= constants.superblock_copies * superblock_trailer_client_table_size_max;
347
368
  size -= constants.superblock_copies * superblock_trailer_manifest_size_max;
348
369
  size -= constants.journal_size_max;
@@ -366,11 +387,11 @@ comptime {
366
387
  assert(block_count_max * constants.block_size + data_file_size_min <= constants.storage_size_max);
367
388
  }
368
389
 
369
- /// This table shows the sequence number progression of the SuperBlock's sectors.
390
+ /// This table shows the sequence number progression of the SuperBlock's headers.
370
391
  ///
371
392
  /// action working staging disk
372
393
  /// format seq seq seq
373
- /// 0 - Initially the file has no sectors.
394
+ /// 0 - Initially the file has no headers.
374
395
  /// 0 1 -
375
396
  /// 0 1 1 Write a copyset for the first sequence.
376
397
  /// 1 1 1 Read quorum; verify 3/4 are valid.
@@ -418,7 +439,9 @@ pub fn SuperBlockType(comptime Storage: type) type {
418
439
  read_threshold: ?Quorums.Threshold = null,
419
440
  copy: ?u8 = null,
420
441
  /// Used by format(), checkpoint(), and view_change().
421
- vsr_state: ?SuperBlockSector.VSRState = null,
442
+ vsr_state: ?SuperBlockHeader.VSRState = null,
443
+ /// Used by format() and view_change().
444
+ vsr_headers: ?vsr.ViewChangeHeaders.BoundedArray = null,
422
445
  repairs: ?Quorums.RepairIterator = null, // Used by open().
423
446
  };
424
447
 
@@ -431,15 +454,15 @@ pub fn SuperBlockType(comptime Storage: type) type {
431
454
  storage_size: u64 = superblock_zone_size,
432
455
 
433
456
  /// The superblock that was recovered at startup after a crash or that was last written.
434
- working: *align(constants.sector_size) SuperBlockSector,
457
+ working: *align(constants.sector_size) SuperBlockHeader,
435
458
 
436
459
  /// The superblock that will replace the current working superblock once written.
437
460
  /// We cannot mutate any working state directly until it is safely on stable storage.
438
461
  /// Otherwise, we may accidentally externalize guarantees that are not yet durable.
439
- staging: *align(constants.sector_size) SuperBlockSector,
462
+ staging: *align(constants.sector_size) SuperBlockHeader,
440
463
 
441
464
  /// The copies that we read into at startup or when verifying the written superblock.
442
- reading: []align(constants.sector_size) SuperBlockSector,
465
+ reading: []align(constants.sector_size) SuperBlockHeader,
443
466
 
444
467
  /// It might seem that, at startup, we simply install the copy with the highest sequence.
445
468
  ///
@@ -474,7 +497,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
474
497
  /// other operations, only the following queue combinations are allowed:
475
498
  /// 1. A view change may queue on a checkpoint.
476
499
  /// 2. A checkpoint may queue on a view change.
477
- ///
500
+ ///
478
501
  /// There may only be a single caller queued at a time, to ensure that the VSR protocol is
479
502
  /// careful to submit at most one view change at a time.
480
503
  queue_head: ?*Context = null,
@@ -498,14 +521,14 @@ pub fn SuperBlockType(comptime Storage: type) type {
498
521
  const block_count_limit = shard_count_limit * FreeSet.shard_bits;
499
522
  assert(block_count_limit <= block_count_max);
500
523
 
501
- const a = try allocator.allocAdvanced(SuperBlockSector, constants.sector_size, 1, .exact);
524
+ const a = try allocator.allocAdvanced(SuperBlockHeader, constants.sector_size, 1, .exact);
502
525
  errdefer allocator.free(a);
503
526
 
504
- const b = try allocator.allocAdvanced(SuperBlockSector, constants.sector_size, 1, .exact);
527
+ const b = try allocator.allocAdvanced(SuperBlockHeader, constants.sector_size, 1, .exact);
505
528
  errdefer allocator.free(b);
506
529
 
507
530
  const reading = try allocator.allocAdvanced(
508
- [constants.superblock_copies]SuperBlockSector,
531
+ [constants.superblock_copies]SuperBlockHeader,
509
532
  constants.sector_size,
510
533
  1,
511
534
  .exact,
@@ -615,16 +638,18 @@ pub fn SuperBlockType(comptime Storage: type) type {
615
638
  .commit_min_checksum = 0,
616
639
  .commit_min = 0,
617
640
  .commit_max = 0,
618
- .view_normal = 0,
641
+ .log_view = 0,
619
642
  .view = 0,
620
643
  },
621
644
  .snapshots = undefined,
622
645
  .manifest_size = 0,
623
646
  .free_set_size = 0,
624
647
  .client_table_size = 0,
648
+ .vsr_headers_count = 0,
649
+ .vsr_headers_all = mem.zeroes([constants.view_change_headers_max]vsr.Header),
625
650
  };
626
651
 
627
- mem.set(SuperBlockSector.Snapshot, &superblock.working.snapshots, .{
652
+ mem.set(SuperBlockHeader.Snapshot, &superblock.working.snapshots, .{
628
653
  .created = 0,
629
654
  .queried = 0,
630
655
  .timeout = 0,
@@ -632,11 +657,15 @@ pub fn SuperBlockType(comptime Storage: type) type {
632
657
 
633
658
  superblock.working.set_checksum();
634
659
 
660
+ var vsr_headers = vsr.ViewChangeHeaders.BoundedArray{ .buffer = undefined };
661
+ vsr_headers.appendAssumeCapacity(vsr.Header.root_prepare(options.cluster));
662
+
635
663
  context.* = .{
636
664
  .superblock = superblock,
637
665
  .callback = callback,
638
666
  .caller = .format,
639
- .vsr_state = SuperBlockSector.VSRState.root(options.cluster),
667
+ .vsr_state = SuperBlockHeader.VSRState.root(options.cluster),
668
+ .vsr_headers = vsr_headers,
640
669
  };
641
670
 
642
671
  // TODO At a higher layer, we must:
@@ -662,21 +691,33 @@ pub fn SuperBlockType(comptime Storage: type) type {
662
691
  superblock.acquire(context);
663
692
  }
664
693
 
694
+ const UpdateCheckpoint = struct {
695
+ commit_min_checksum: u128,
696
+ commit_min: u64,
697
+ commit_max: u64,
698
+ };
699
+
665
700
  /// The vsr_state must update the commit_min and commit_min_checksum.
666
- // TODO Will the replica ever update view/view_normal by calling checkpoint() during a view
667
- // change? If not, forbid it.
701
+ /// The vsr_state must not update view/log_view.
668
702
  pub fn checkpoint(
669
703
  superblock: *SuperBlock,
670
704
  callback: fn (context: *Context) void,
671
705
  context: *Context,
672
- vsr_state: SuperBlockSector.VSRState,
706
+ update: UpdateCheckpoint,
673
707
  ) void {
674
708
  assert(superblock.opened);
675
- // Checkpoint must advance commit_min, but never the view.
709
+ assert(superblock.staging.vsr_state.commit_min < update.commit_min);
710
+ assert(superblock.staging.vsr_state.commit_min_checksum != update.commit_min_checksum);
711
+ assert(update.commit_min <= update.commit_max);
712
+
713
+ const vsr_state = SuperBlockHeader.VSRState{
714
+ .commit_min_checksum = update.commit_min_checksum,
715
+ .commit_min = update.commit_min,
716
+ .commit_max = update.commit_max,
717
+ .log_view = superblock.staging.vsr_state.log_view,
718
+ .view = superblock.staging.vsr_state.view,
719
+ };
676
720
  assert(superblock.staging.vsr_state.would_be_updated_by(vsr_state));
677
- assert(superblock.staging.vsr_state.commit_min < vsr_state.commit_min);
678
- assert(superblock.staging.vsr_state.commit_min_checksum !=
679
- vsr_state.commit_min_checksum);
680
721
 
681
722
  context.* = .{
682
723
  .superblock = superblock,
@@ -688,59 +729,71 @@ pub fn SuperBlockType(comptime Storage: type) type {
688
729
  superblock.acquire(context);
689
730
  }
690
731
 
691
- /// The vsr_state must not update the `commit_min` or `commit_min_checksum`.
732
+ const UpdateViewChange = struct {
733
+ commit_max: u64,
734
+ log_view: u32,
735
+ view: u32,
736
+ headers: vsr.ViewChangeHeaders.BoundedArray,
737
+ };
738
+
739
+ /// The replica calls view_change() to persist its view/log_view — it cannot
740
+ /// advertise either value until it is certain they will never backtrack.
741
+ ///
742
+ /// The update must advance view/log_view (monotonically increasing).
692
743
  pub fn view_change(
693
744
  superblock: *SuperBlock,
694
745
  callback: fn (context: *Context) void,
695
746
  context: *Context,
696
- vsr_state: SuperBlockSector.VSRState,
747
+ update: UpdateViewChange,
697
748
  ) void {
698
749
  assert(superblock.opened);
699
- assert(vsr_state.commit_min == superblock.staging.vsr_state.commit_min);
700
- assert(vsr_state.commit_min_checksum ==
701
- superblock.staging.vsr_state.commit_min_checksum);
750
+ assert(superblock.staging.vsr_state.commit_min <= update.headers.get(0).op);
751
+ assert(superblock.staging.vsr_state.commit_max <= update.commit_max);
752
+ assert(superblock.staging.vsr_state.view <= update.view);
753
+ assert(superblock.staging.vsr_state.log_view <= update.log_view);
754
+ assert(superblock.staging.vsr_state.log_view < update.log_view or
755
+ superblock.staging.vsr_state.view < update.view);
756
+
757
+ vsr.ViewChangeHeaders.verify(update.headers.constSlice());
758
+ assert(update.view >= update.log_view);
759
+
760
+ const vsr_state = SuperBlockHeader.VSRState{
761
+ .commit_min_checksum = superblock.staging.vsr_state.commit_min_checksum,
762
+ .commit_min = superblock.staging.vsr_state.commit_min,
763
+ .commit_max = update.commit_max,
764
+ .log_view = update.log_view,
765
+ .view = update.view,
766
+ };
767
+ assert(vsr_state.internally_consistent());
768
+ assert(superblock.staging.vsr_state.would_be_updated_by(vsr_state));
702
769
  assert(superblock.staging.vsr_state.monotonic(vsr_state));
703
770
 
704
- log.debug(
705
- "view_change: commit_min_checksum={}..{} commit_min={}..{} commit_max={}..{} " ++
706
- "view_normal={}..{} view={}..{}",
707
- .{
708
- superblock.staging.vsr_state.commit_min_checksum,
709
- vsr_state.commit_min_checksum,
710
-
711
- superblock.staging.vsr_state.commit_min,
712
- vsr_state.commit_min,
771
+ log.debug("view_change: commit_max={}..{} log_view={}..{} view={}..{} head={}..{}", .{
772
+ superblock.staging.vsr_state.commit_max,
773
+ update.commit_max,
713
774
 
714
- superblock.staging.vsr_state.commit_max,
715
- vsr_state.commit_max,
775
+ superblock.staging.vsr_state.log_view,
776
+ update.log_view,
716
777
 
717
- superblock.staging.vsr_state.view_normal,
718
- vsr_state.view_normal,
719
-
720
- superblock.staging.vsr_state.view,
721
- vsr_state.view,
722
- },
723
- );
778
+ superblock.staging.vsr_state.view,
779
+ update.view,
724
780
 
725
- assert(vsr_state.internally_consistent());
781
+ superblock.staging.vsr_headers().slice[0].checksum,
782
+ update.headers.get(0).checksum,
783
+ });
726
784
 
727
785
  context.* = .{
728
786
  .superblock = superblock,
729
787
  .callback = callback,
730
788
  .caller = .view_change,
731
789
  .vsr_state = vsr_state,
790
+ .vsr_headers = update.headers,
732
791
  };
733
792
 
734
- if (!superblock.staging.vsr_state.would_be_updated_by(context.vsr_state.?)) {
735
- log.debug("view_change: no change", .{});
736
- callback(context);
737
- return;
738
- }
739
-
740
793
  superblock.acquire(context);
741
794
  }
742
795
 
743
- pub fn view_change_in_progress(superblock: *SuperBlock) bool {
796
+ pub fn view_change_in_progress(superblock: *const SuperBlock) bool {
744
797
  assert(superblock.opened);
745
798
 
746
799
  if (superblock.queue_head) |head| {
@@ -768,7 +821,26 @@ pub fn SuperBlockType(comptime Storage: type) type {
768
821
  superblock.staging.* = superblock.working.*;
769
822
  superblock.staging.sequence = superblock.staging.sequence + 1;
770
823
  superblock.staging.parent = superblock.staging.checksum;
771
- superblock.staging.vsr_state.update(context.vsr_state.?);
824
+ superblock.staging.vsr_state = context.vsr_state.?;
825
+
826
+ if (context.vsr_headers) |headers| {
827
+ assert(context.caller == .format or context.caller == .view_change);
828
+
829
+ superblock.staging.vsr_headers_count = @intCast(u32, headers.len);
830
+ stdx.copy_disjoint(
831
+ .exact,
832
+ vsr.Header,
833
+ superblock.staging.vsr_headers_all[0..headers.len],
834
+ headers.constSlice(),
835
+ );
836
+ std.mem.set(
837
+ vsr.Header,
838
+ superblock.staging.vsr_headers_all[headers.len..],
839
+ std.mem.zeroes(vsr.Header),
840
+ );
841
+ } else {
842
+ assert(context.caller == .checkpoint);
843
+ }
772
844
 
773
845
  if (context.caller != .view_change) {
774
846
  superblock.write_staging_encode_manifest();
@@ -779,14 +851,14 @@ pub fn SuperBlockType(comptime Storage: type) type {
779
851
 
780
852
  context.copy = 0;
781
853
  if (context.caller == .view_change) {
782
- superblock.write_sector(context);
854
+ superblock.write_header(context);
783
855
  } else {
784
856
  superblock.write_manifest(context);
785
857
  }
786
858
  }
787
859
 
788
860
  fn write_staging_encode_manifest(superblock: *SuperBlock) void {
789
- const staging: *SuperBlockSector = superblock.staging;
861
+ const staging: *SuperBlockHeader = superblock.staging;
790
862
  const target = superblock.manifest_buffer;
791
863
 
792
864
  staging.manifest_size = @intCast(u32, superblock.manifest.encode(target));
@@ -794,7 +866,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
794
866
  }
795
867
 
796
868
  fn write_staging_encode_free_set(superblock: *SuperBlock) void {
797
- const staging: *SuperBlockSector = superblock.staging;
869
+ const staging: *SuperBlockHeader = superblock.staging;
798
870
  const encode_size_max = FreeSet.encode_size_max(superblock.block_count_limit);
799
871
  const target = superblock.free_set_buffer[0..encode_size_max];
800
872
 
@@ -824,7 +896,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
824
896
  }
825
897
 
826
898
  fn write_staging_encode_client_table(superblock: *SuperBlock) void {
827
- const staging: *SuperBlockSector = superblock.staging;
899
+ const staging: *SuperBlockHeader = superblock.staging;
828
900
  const target = superblock.client_table_buffer;
829
901
 
830
902
  staging.client_table_size = @intCast(u32, superblock.client_table.encode(target));
@@ -838,7 +910,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
838
910
  assert(size <= superblock_trailer_manifest_size_max);
839
911
 
840
912
  const buffer = superblock.manifest_buffer[0..size];
841
- const offset = Layout.offset_manifest(context.copy.?);
913
+ const offset = areas.manifest.offset(context.copy.?);
842
914
 
843
915
  mem.set(u8, buffer[superblock.staging.manifest_size..], 0); // Zero sector padding.
844
916
 
@@ -881,7 +953,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
881
953
  assert(size <= superblock_trailer_free_set_size_max);
882
954
 
883
955
  const buffer = superblock.free_set_buffer[0..size];
884
- const offset = Layout.offset_free_set(context.copy.?);
956
+ const offset = areas.free_set.offset(context.copy.?);
885
957
 
886
958
  mem.set(u8, buffer[superblock.staging.free_set_size..], 0); // Zero sector padding.
887
959
 
@@ -924,7 +996,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
924
996
  assert(size <= superblock_trailer_client_table_size_max);
925
997
 
926
998
  const buffer = superblock.client_table_buffer[0..size];
927
- const offset = Layout.offset_client_table(context.copy.?);
999
+ const offset = areas.client_table.offset(context.copy.?);
928
1000
 
929
1001
  mem.set(u8, buffer[superblock.staging.client_table_size..], 0); // Zero sector padding.
930
1002
 
@@ -957,10 +1029,10 @@ pub fn SuperBlockType(comptime Storage: type) type {
957
1029
 
958
1030
  fn write_client_table_callback(write: *Storage.Write) void {
959
1031
  const context = @fieldParentPtr(Context, "write", write);
960
- context.superblock.write_sector(context);
1032
+ context.superblock.write_header(context);
961
1033
  }
962
1034
 
963
- fn write_sector(superblock: *SuperBlock, context: *Context) void {
1035
+ fn write_header(superblock: *SuperBlock, context: *Context) void {
964
1036
  assert(superblock.queue_head == context);
965
1037
 
966
1038
  // We update the working superblock for a checkpoint/format/view_change:
@@ -986,9 +1058,9 @@ pub fn SuperBlockType(comptime Storage: type) type {
986
1058
  assert(superblock.staging.valid_checksum());
987
1059
 
988
1060
  const buffer = mem.asBytes(superblock.staging);
989
- const offset = Layout.offset_sector(context.copy.?);
1061
+ const offset = areas.header.offset(context.copy.?);
990
1062
 
991
- log.debug("{}: {s}: write_sector: checksum={x} sequence={} copy={} size={} offset={}", .{
1063
+ log.debug("{}: {s}: write_header: checksum={x} sequence={} copy={} size={} offset={}", .{
992
1064
  superblock.staging.replica,
993
1065
  @tagName(context.caller),
994
1066
  superblock.staging.checksum,
@@ -1001,7 +1073,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
1001
1073
  superblock.assert_bounds(offset, buffer.len);
1002
1074
 
1003
1075
  superblock.storage.write_sectors(
1004
- write_sector_callback,
1076
+ write_header_callback,
1005
1077
  &context.write,
1006
1078
  buffer,
1007
1079
  .superblock,
@@ -1009,7 +1081,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
1009
1081
  );
1010
1082
  }
1011
1083
 
1012
- fn write_sector_callback(write: *Storage.Write) void {
1084
+ fn write_header_callback(write: *Storage.Write) void {
1013
1085
  const context = @fieldParentPtr(Context, "write", write);
1014
1086
  const superblock = context.superblock;
1015
1087
  const copy = context.copy.?;
@@ -1034,7 +1106,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
1034
1106
  switch (context.caller) {
1035
1107
  .open => unreachable,
1036
1108
  .format, .checkpoint => superblock.write_manifest(context),
1037
- .view_change => superblock.write_sector(context),
1109
+ .view_change => superblock.write_header(context),
1038
1110
  }
1039
1111
  }
1040
1112
  }
@@ -1056,18 +1128,18 @@ pub fn SuperBlockType(comptime Storage: type) type {
1056
1128
  context.copy = 0;
1057
1129
  context.read_threshold = threshold;
1058
1130
  for (superblock.reading) |*copy| copy.* = undefined;
1059
- superblock.read_sector(context);
1131
+ superblock.read_header(context);
1060
1132
  }
1061
1133
 
1062
- fn read_sector(superblock: *SuperBlock, context: *Context) void {
1134
+ fn read_header(superblock: *SuperBlock, context: *Context) void {
1063
1135
  assert(superblock.queue_head == context);
1064
1136
  assert(context.copy.? < constants.superblock_copies);
1065
1137
  assert(context.read_threshold != null);
1066
1138
 
1067
1139
  const buffer = mem.asBytes(&superblock.reading[context.copy.?]);
1068
- const offset = Layout.offset_sector(context.copy.?);
1140
+ const offset = areas.header.offset(context.copy.?);
1069
1141
 
1070
- log.debug("{s}: read_sector: copy={} size={} offset={}", .{
1142
+ log.debug("{s}: read_header: copy={} size={} offset={}", .{
1071
1143
  @tagName(context.caller),
1072
1144
  context.copy.?,
1073
1145
  buffer.len,
@@ -1077,7 +1149,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
1077
1149
  superblock.assert_bounds(offset, buffer.len);
1078
1150
 
1079
1151
  superblock.storage.read_sectors(
1080
- read_sector_callback,
1152
+ read_header_callback,
1081
1153
  &context.read,
1082
1154
  buffer,
1083
1155
  .superblock,
@@ -1085,7 +1157,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
1085
1157
  );
1086
1158
  }
1087
1159
 
1088
- fn read_sector_callback(read: *Storage.Read) void {
1160
+ fn read_header_callback(read: *Storage.Read) void {
1089
1161
  const context = @fieldParentPtr(Context, "read", read);
1090
1162
  const superblock = context.superblock;
1091
1163
  const threshold = context.read_threshold.?;
@@ -1095,7 +1167,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
1095
1167
  assert(context.copy.? < constants.superblock_copies);
1096
1168
  if (context.copy.? + 1 != constants.superblock_copies) {
1097
1169
  context.copy = context.copy.? + 1;
1098
- superblock.read_sector(context);
1170
+ superblock.read_header(context);
1099
1171
  return;
1100
1172
  }
1101
1173
 
@@ -1105,9 +1177,9 @@ pub fn SuperBlockType(comptime Storage: type) type {
1105
1177
  if (superblock.quorums.working(superblock.reading, threshold)) |quorum| {
1106
1178
  assert(quorum.valid);
1107
1179
  assert(quorum.copies.count() >= threshold.count());
1108
- assert(quorum.sector.storage_size_max == constants.storage_size_max);
1180
+ assert(quorum.header.storage_size_max == constants.storage_size_max);
1109
1181
 
1110
- const working = quorum.sector;
1182
+ const working = quorum.header;
1111
1183
  if (threshold == .verify) {
1112
1184
  if (working.checksum != superblock.staging.checksum) {
1113
1185
  @panic("superblock failed verification after writing");
@@ -1125,8 +1197,9 @@ pub fn SuperBlockType(comptime Storage: type) type {
1125
1197
  vsr.Header.root_prepare(working.cluster).checksum);
1126
1198
  assert(working.vsr_state.commit_min == 0);
1127
1199
  assert(working.vsr_state.commit_max == 0);
1128
- assert(working.vsr_state.view_normal == 0);
1200
+ assert(working.vsr_state.log_view == 0);
1129
1201
  assert(working.vsr_state.view == 0);
1202
+ assert(working.vsr_headers_count == 1);
1130
1203
  } else if (context.caller == .checkpoint) {
1131
1204
  superblock.free_set.checkpoint();
1132
1205
  }
@@ -1137,7 +1210,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
1137
1210
  "{s}: installed working superblock: checksum={x} sequence={} cluster={} " ++
1138
1211
  "replica={} size={} " ++
1139
1212
  "commit_min_checksum={} commit_min={} commit_max={} " ++
1140
- "view_normal={} view={}",
1213
+ "log_view={} view={}",
1141
1214
  .{
1142
1215
  @tagName(context.caller),
1143
1216
  superblock.working.checksum,
@@ -1148,10 +1221,17 @@ pub fn SuperBlockType(comptime Storage: type) type {
1148
1221
  superblock.working.vsr_state.commit_min_checksum,
1149
1222
  superblock.working.vsr_state.commit_min,
1150
1223
  superblock.working.vsr_state.commit_max,
1151
- superblock.working.vsr_state.view_normal,
1224
+ superblock.working.vsr_state.log_view,
1152
1225
  superblock.working.vsr_state.view,
1153
1226
  },
1154
1227
  );
1228
+ for (superblock.working.vsr_headers().slice) |*header| {
1229
+ log.debug("{s}: vsr_header: op={} checksum={}", .{
1230
+ @tagName(context.caller),
1231
+ header.op,
1232
+ header.checksum,
1233
+ });
1234
+ }
1155
1235
 
1156
1236
  if (context.caller == .open) {
1157
1237
  if (context.repairs) |_| {
@@ -1187,7 +1267,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
1187
1267
  assert(size <= superblock_trailer_manifest_size_max);
1188
1268
 
1189
1269
  const buffer = superblock.manifest_buffer[0..size];
1190
- const offset = Layout.offset_manifest(context.copy.?);
1270
+ const offset = areas.manifest.offset(context.copy.?);
1191
1271
 
1192
1272
  log.debug("{s}: read_manifest: copy={} size={} offset={}", .{
1193
1273
  @tagName(context.caller),
@@ -1236,12 +1316,14 @@ pub fn SuperBlockType(comptime Storage: type) type {
1236
1316
  // We do not repair padding.
1237
1317
  context.copy = 0;
1238
1318
  superblock.read_free_set(context);
1239
- } else if (copy + 1 == constants.superblock_copies) {
1240
- @panic("superblock manifest lost");
1241
1319
  } else {
1242
1320
  log.debug("open: read_manifest: corrupt copy={}", .{copy});
1243
- context.copy = copy + 1;
1244
- superblock.read_manifest(context);
1321
+ if (copy + 1 == constants.superblock_copies) {
1322
+ @panic("superblock manifest lost");
1323
+ } else {
1324
+ context.copy = copy + 1;
1325
+ superblock.read_manifest(context);
1326
+ }
1245
1327
  }
1246
1328
  }
1247
1329
 
@@ -1254,7 +1336,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
1254
1336
  assert(size <= superblock_trailer_free_set_size_max);
1255
1337
 
1256
1338
  const buffer = superblock.free_set_buffer[0..size];
1257
- const offset = Layout.offset_free_set(context.copy.?);
1339
+ const offset = areas.free_set.offset(context.copy.?);
1258
1340
 
1259
1341
  log.debug("{s}: read_free_set: copy={} size={} offset={}", .{
1260
1342
  @tagName(context.caller),
@@ -1328,7 +1410,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
1328
1410
  assert(size <= superblock_trailer_client_table_size_max);
1329
1411
 
1330
1412
  const buffer = superblock.client_table_buffer[0..size];
1331
- const offset = Layout.offset_client_table(context.copy.?);
1413
+ const offset = areas.client_table.offset(context.copy.?);
1332
1414
 
1333
1415
  log.debug("{s}: read_client_table: copy={} size={} offset={}", .{
1334
1416
  @tagName(context.caller),
@@ -1497,32 +1579,48 @@ pub fn SuperBlockType(comptime Storage: type) type {
1497
1579
  };
1498
1580
  }
1499
1581
 
1500
- pub const Layout = struct {
1501
- pub fn offset_sector(copy: u8) u64 {
1502
- assert(copy < constants.superblock_copies);
1503
- return superblock_copy_size * @as(u64, copy);
1504
- }
1582
+ pub const Area = enum {
1583
+ header,
1584
+ manifest,
1585
+ free_set,
1586
+ client_table,
1587
+ };
1505
1588
 
1506
- pub fn offset_manifest(copy: u8) u64 {
1507
- assert(copy < constants.superblock_copies);
1508
- return offset_sector(copy) + @sizeOf(SuperBlockSector);
1509
- }
1589
+ pub const areas = struct {
1590
+ pub const header = AreaRange{
1591
+ .base = 0,
1592
+ .size_max = @sizeOf(SuperBlockHeader),
1593
+ };
1510
1594
 
1511
- pub fn offset_free_set(copy: u8) u64 {
1512
- assert(copy < constants.superblock_copies);
1513
- return offset_manifest(copy) + superblock_trailer_manifest_size_max;
1514
- }
1595
+ pub const manifest = AreaRange{
1596
+ .base = header.base + header.size_max,
1597
+ .size_max = superblock_trailer_manifest_size_max, // TODO inline these constants?
1598
+ };
1515
1599
 
1516
- pub fn offset_client_table(copy: u8) u64 {
1517
- assert(copy < constants.superblock_copies);
1518
- return offset_free_set(copy) + superblock_trailer_free_set_size_max;
1519
- }
1600
+ pub const free_set = AreaRange{
1601
+ .base = manifest.base + manifest.size_max,
1602
+ .size_max = superblock_trailer_free_set_size_max,
1603
+ };
1604
+
1605
+ pub const client_table = AreaRange{
1606
+ .base = free_set.base + free_set.size_max,
1607
+ .size_max = superblock_trailer_client_table_size_max,
1608
+ };
1609
+
1610
+ const AreaRange = struct {
1611
+ base: u64,
1612
+ size_max: u64,
1613
+
1614
+ pub fn offset(area: AreaRange, copy: u8) u64 {
1615
+ return superblock_copy_size * @as(u64, copy) + area.base;
1616
+ }
1617
+ };
1520
1618
  };
1521
1619
 
1522
- test "SuperBlockSector" {
1620
+ test "SuperBlockHeader" {
1523
1621
  const expect = std.testing.expect;
1524
1622
 
1525
- var a = std.mem.zeroInit(SuperBlockSector, .{});
1623
+ var a = std.mem.zeroes(SuperBlockHeader);
1526
1624
  a.set_checksum();
1527
1625
 
1528
1626
  assert(a.copy == 0);