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
@@ -4,8 +4,8 @@ const assert = std.debug.assert;
4
4
  const math = std.math;
5
5
  const mem = std.mem;
6
6
 
7
- const util = @import("../util.zig");
8
- const div_ceil = @import("../util.zig").div_ceil;
7
+ const stdx = @import("../stdx.zig");
8
+ const div_ceil = @import("../stdx.zig").div_ceil;
9
9
  const binary_search_values_raw = @import("binary_search.zig").binary_search_values_raw;
10
10
  const binary_search_keys = @import("binary_search.zig").binary_search_keys;
11
11
  const Direction = @import("direction.zig").Direction;
@@ -280,13 +280,13 @@ fn SegmentedArrayType(
280
280
 
281
281
  const total = array.count(a) + @intCast(u32, elements.len);
282
282
  if (total <= node_capacity) {
283
- util.copy_right(
283
+ stdx.copy_right(
284
284
  .inexact,
285
285
  T,
286
286
  a_pointer[cursor.relative_index + elements.len ..],
287
287
  a_pointer[cursor.relative_index..array.count(a)],
288
288
  );
289
- util.copy_disjoint(.inexact, T, a_pointer[cursor.relative_index..], elements);
289
+ stdx.copy_disjoint(.inexact, T, a_pointer[cursor.relative_index..], elements);
290
290
 
291
291
  array.increment_indexes_after(a, @intCast(u32, elements.len));
292
292
  return;
@@ -348,7 +348,7 @@ fn SegmentedArrayType(
348
348
 
349
349
  if (a_half < cursor.relative_index) {
350
350
  // Move the part of `a` that is past the half-way point into `b`.
351
- util.copy_right(
351
+ stdx.copy_right(
352
352
  .inexact,
353
353
  T,
354
354
  b_half_pointer,
@@ -383,10 +383,10 @@ fn SegmentedArrayType(
383
383
  const source_a = source[0..target_a.len];
384
384
  const source_b = source[target_a.len..];
385
385
  if (target_b.ptr != source_b.ptr) {
386
- util.copy_right(.exact, T, target_b, source_b);
386
+ stdx.copy_right(.exact, T, target_b, source_b);
387
387
  }
388
388
  if (target_a.ptr != source_a.ptr) {
389
- util.copy_right(.exact, T, target_a, source_a);
389
+ stdx.copy_right(.exact, T, target_a, source_a);
390
390
  }
391
391
  }
392
392
 
@@ -395,13 +395,13 @@ fn SegmentedArrayType(
395
395
  assert(node <= array.node_count);
396
396
  assert(array.node_count + 1 <= node_count_max);
397
397
 
398
- util.copy_right(
398
+ stdx.copy_right(
399
399
  .exact,
400
400
  ?*[node_capacity]T,
401
401
  array.nodes[node + 1 .. array.node_count + 1],
402
402
  array.nodes[node..array.node_count],
403
403
  );
404
- util.copy_right(
404
+ stdx.copy_right(
405
405
  .exact,
406
406
  u32,
407
407
  array.indexes[node + 1 .. array.node_count + 2],
@@ -470,7 +470,7 @@ fn SegmentedArrayType(
470
470
 
471
471
  // Remove elements from exactly one node:
472
472
  if (a_remaining + remove_count <= array.count(a)) {
473
- util.copy_left(
473
+ stdx.copy_left(
474
474
  .inexact,
475
475
  T,
476
476
  a_pointer[a_remaining..],
@@ -497,7 +497,7 @@ fn SegmentedArrayType(
497
497
  assert(a_remaining > 0 or b_remaining.len > 0);
498
498
 
499
499
  if (a_remaining >= half) {
500
- util.copy_left(.inexact, T, b_pointer, b_remaining);
500
+ stdx.copy_left(.inexact, T, b_pointer, b_remaining);
501
501
 
502
502
  array.indexes[b] = array.indexes[a] + a_remaining;
503
503
  array.decrement_indexes_after(b, remove_count);
@@ -514,7 +514,7 @@ fn SegmentedArrayType(
514
514
  assert(a_remaining < half and b_remaining.len < half);
515
515
  assert(a_remaining + b_remaining.len <= node_capacity);
516
516
 
517
- util.copy_disjoint(.inexact, T, a_pointer[a_remaining..], b_remaining);
517
+ stdx.copy_disjoint(.inexact, T, a_pointer[a_remaining..], b_remaining);
518
518
 
519
519
  array.indexes[b] = array.indexes[a] + a_remaining + @intCast(u32, b_remaining.len);
520
520
  array.decrement_indexes_after(b, remove_count);
@@ -576,7 +576,7 @@ fn SegmentedArrayType(
576
576
 
577
577
  const total = array.count(a) + @intCast(u32, b_elements.len);
578
578
  if (total <= node_capacity) {
579
- util.copy_disjoint(.inexact, T, a_pointer[array.count(a)..], b_elements);
579
+ stdx.copy_disjoint(.inexact, T, a_pointer[array.count(a)..], b_elements);
580
580
 
581
581
  array.indexes[b] = array.indexes[b + 1];
582
582
  array.remove_empty_node_at(node_pool, b);
@@ -588,13 +588,13 @@ fn SegmentedArrayType(
588
588
  assert(a_half >= b_half);
589
589
  assert(a_half + b_half == total);
590
590
 
591
- util.copy_disjoint(
591
+ stdx.copy_disjoint(
592
592
  .exact,
593
593
  T,
594
594
  a_pointer[array.count(a)..a_half],
595
595
  b_elements[0 .. a_half - array.count(a)],
596
596
  );
597
- util.copy_left(.inexact, T, b_pointer, b_elements[a_half - array.count(a) ..]);
597
+ stdx.copy_left(.inexact, T, b_pointer, b_elements[a_half - array.count(a) ..]);
598
598
 
599
599
  array.indexes[b] = array.indexes[a] + a_half;
600
600
 
@@ -616,13 +616,13 @@ fn SegmentedArrayType(
616
616
  @ptrCast(NodePool.Node, @alignCast(NodePool.node_alignment, array.nodes[node].?)),
617
617
  );
618
618
 
619
- util.copy_left(
619
+ stdx.copy_left(
620
620
  .exact,
621
621
  ?*[node_capacity]T,
622
622
  array.nodes[node .. array.node_count - 1],
623
623
  array.nodes[node + 1 .. array.node_count],
624
624
  );
625
- util.copy_left(
625
+ stdx.copy_left(
626
626
  .exact,
627
627
  u32,
628
628
  array.indexes[node..array.node_count],
@@ -1,6 +1,6 @@
1
1
  const std = @import("std");
2
2
 
3
- const fuzz = @import("../test/fuzz.zig");
3
+ const fuzz = @import("../testing/fuzz.zig");
4
4
  const segmented_array = @import("segmented_array.zig");
5
5
 
6
6
  pub fn main() !void {
@@ -8,7 +8,7 @@ const meta = std.meta;
8
8
  const Vector = meta.Vector;
9
9
 
10
10
  const constants = @import("../constants.zig");
11
- const div_ceil = @import("../util.zig").div_ceil;
11
+ const div_ceil = @import("../stdx.zig").div_ceil;
12
12
  const verify = constants.verify;
13
13
 
14
14
  pub const Layout = struct {
@@ -8,12 +8,13 @@ const vsr = @import("../vsr.zig");
8
8
  const binary_search = @import("binary_search.zig");
9
9
  const bloom_filter = @import("bloom_filter.zig");
10
10
 
11
- const util = @import("../util.zig");
12
- const div_ceil = util.div_ceil;
11
+ const stdx = @import("../stdx.zig");
12
+ const div_ceil = stdx.div_ceil;
13
13
  const eytzinger = @import("eytzinger.zig").eytzinger;
14
14
  const snapshot_latest = @import("tree.zig").snapshot_latest;
15
15
 
16
16
  const BlockType = @import("grid.zig").BlockType;
17
+ const alloc_block = @import("grid.zig").alloc_block;
17
18
  const TableInfoType = @import("manifest.zig").TableInfoType;
18
19
 
19
20
  pub const TableUsage = enum {
@@ -484,13 +485,13 @@ pub fn TableType(
484
485
  data_blocks_in_filter: u32 = 0,
485
486
 
486
487
  pub fn init(allocator: mem.Allocator) !Builder {
487
- const index_block = try allocator.alignedAlloc(u8, constants.sector_size, block_size);
488
+ const index_block = try alloc_block(allocator);
488
489
  errdefer allocator.free(index_block);
489
490
 
490
- const filter_block = try allocator.alignedAlloc(u8, constants.sector_size, block_size);
491
+ const filter_block = try alloc_block(allocator);
491
492
  errdefer allocator.free(filter_block);
492
493
 
493
- const data_block = try allocator.alignedAlloc(u8, constants.sector_size, block_size);
494
+ const data_block = try alloc_block(allocator);
494
495
  errdefer allocator.free(data_block);
495
496
 
496
497
  return Builder{
@@ -527,7 +528,7 @@ pub fn TableType(
527
528
  const values_max = data_block_values(builder.data_block);
528
529
  assert(values_max.len == data.value_count_max);
529
530
 
530
- util.copy_disjoint(.inexact, Value, values_max[builder.value..], values);
531
+ stdx.copy_disjoint(.inexact, Value, values_max[builder.value..], values);
531
532
  builder.value += @intCast(u32, values.len);
532
533
 
533
534
  for (values) |*value| {
@@ -596,8 +597,6 @@ pub fn TableType(
596
597
 
597
598
  const values_padding = mem.sliceAsBytes(values_max[builder.value..]);
598
599
  const block_padding = block[data.padding_offset..][0..data.padding_size];
599
- mem.set(u8, values_padding, 0);
600
- mem.set(u8, block_padding, 0);
601
600
  assert(compare_keys(key_from_value(&values[values.len - 1]), key_max) == .eq);
602
601
 
603
602
  const header_bytes = block[0..@sizeOf(vsr.Header)];
@@ -712,17 +711,6 @@ pub fn TableType(
712
711
 
713
712
  const index_block = builder.index_block;
714
713
 
715
- const index_data_keys_padding = index_data_keys(index_block)[builder.data_block_count..];
716
- const index_data_keys_padding_bytes = mem.sliceAsBytes(index_data_keys_padding);
717
- mem.set(u8, index_data_keys_padding_bytes, 0);
718
- mem.set(u64, index_data_addresses(index_block)[builder.data_block_count..], 0);
719
- mem.set(u128, index_data_checksums(index_block)[builder.data_block_count..], 0);
720
-
721
- mem.set(u64, index_filter_addresses(index_block)[builder.filter_block_count..], 0);
722
- mem.set(u128, index_filter_checksums(index_block)[builder.filter_block_count..], 0);
723
-
724
- mem.set(u8, index_block[index.padding_offset..][0..index.padding_size], 0);
725
-
726
714
  const header_bytes = index_block[0..@sizeOf(vsr.Header)];
727
715
  const header = mem.bytesAsValue(vsr.Header, header_bytes);
728
716
 
@@ -992,7 +980,7 @@ pub fn TableType(
992
980
  key_min: ?Key,
993
981
  key_max: ?Key,
994
982
  ) void {
995
- if (Storage != @import("../test/storage.zig").Storage)
983
+ if (Storage != @import("../testing/storage.zig").Storage)
996
984
  // Too complicated to do async verification
997
985
  return;
998
986
 
@@ -4,7 +4,7 @@ const math = std.math;
4
4
  const assert = std.debug.assert;
5
5
 
6
6
  const constants = @import("../constants.zig");
7
- const div_ceil = @import("../util.zig").div_ceil;
7
+ const div_ceil = @import("../stdx.zig").div_ceil;
8
8
  const binary_search = @import("binary_search.zig");
9
9
  const snapshot_latest = @import("tree.zig").snapshot_latest;
10
10
 
@@ -5,7 +5,7 @@ const assert = std.debug.assert;
5
5
 
6
6
  const constants = @import("../constants.zig");
7
7
 
8
- const util = @import("../util.zig");
8
+ const stdx = @import("../stdx.zig");
9
9
  const RingBuffer = @import("../ring_buffer.zig").RingBuffer;
10
10
  const ManifestType = @import("manifest.zig").ManifestType;
11
11
  const GridType = @import("grid.zig").GridType;
@@ -208,7 +208,7 @@ pub fn TableIteratorType(comptime Table: type, comptime Storage: type) type {
208
208
 
209
209
  // Copy the bytes read into a buffer owned by the iterator since the Grid
210
210
  // only guarantees the provided pointer to be valid in this callback.
211
- util.copy_disjoint(.exact, u8, it.index_block, block);
211
+ stdx.copy_disjoint(.exact, u8, it.index_block, block);
212
212
 
213
213
  if (it.index_block_callback) |callback| {
214
214
  it.index_block_callback = null;
@@ -240,7 +240,7 @@ pub fn TableIteratorType(comptime Table: type, comptime Storage: type) type {
240
240
 
241
241
  // Copy the bytes read into a buffer owned by the iterator since the Grid
242
242
  // only guarantees the provided pointer to be valid in this callback.
243
- util.copy_disjoint(.exact, u8, it.data_blocks.next_tail().?, block);
243
+ stdx.copy_disjoint(.exact, u8, it.data_blocks.next_tail().?, block);
244
244
 
245
245
  it.data_blocks.advance_tail();
246
246
  it.data_block_index += 1;
@@ -4,7 +4,7 @@ const math = std.math;
4
4
  const assert = std.debug.assert;
5
5
 
6
6
  const constants = @import("../constants.zig");
7
- const div_ceil = @import("../util.zig").div_ceil;
7
+ const div_ceil = @import("../stdx.zig").div_ceil;
8
8
  const SetAssociativeCache = @import("set_associative_cache.zig").SetAssociativeCache;
9
9
 
10
10
  /// Range queries are not supported on the TableMutable, it must first be made immutable.
@@ -43,6 +43,13 @@ pub fn TableMutableType(comptime Table: type) type {
43
43
  value_count_max: u32,
44
44
  values: Values = .{},
45
45
 
46
+ /// Rather than using values.count(), we count how many values we could have had if every
47
+ /// operation had been on a different key. This means that mistakes in calculating
48
+ /// commit_entries_max are much easier to catch when fuzzing, rather than requiring very
49
+ /// specific workloads.
50
+ /// Invariant: value_count_worst_case <= value_count_max
51
+ value_count_worst_case: u32 = 0,
52
+
46
53
  /// This is used to accelerate point lookups and is not used for range queries.
47
54
  /// Secondary index trees used only for range queries can therefore set this to null.
48
55
  ///
@@ -98,6 +105,8 @@ pub fn TableMutableType(comptime Table: type) type {
98
105
  }
99
106
 
100
107
  pub fn put(table: *TableMutable, value: *const Value) void {
108
+ assert(table.value_count_worst_case < table.value_count_max);
109
+ table.value_count_worst_case += 1;
101
110
  switch (usage) {
102
111
  .secondary_index => {
103
112
  const existing = table.values.fetchRemove(value.*);
@@ -123,6 +132,8 @@ pub fn TableMutableType(comptime Table: type) type {
123
132
  }
124
133
 
125
134
  pub fn remove(table: *TableMutable, value: *const Value) void {
135
+ assert(table.value_count_worst_case < table.value_count_max);
136
+ table.value_count_worst_case += 1;
126
137
  switch (usage) {
127
138
  .secondary_index => {
128
139
  const existing = table.values.fetchRemove(value.*);
@@ -152,11 +163,12 @@ pub fn TableMutableType(comptime Table: type) type {
152
163
  /// assumes that none of the batch's keys are already in `table.values`.
153
164
  pub fn can_commit_batch(table: *TableMutable, batch_count: u32) bool {
154
165
  assert(batch_count <= table.value_count_max);
155
- return (table.count() + batch_count) <= table.value_count_max;
166
+ return (table.value_count_worst_case + batch_count) <= table.value_count_max;
156
167
  }
157
168
 
158
169
  pub fn clear(table: *TableMutable) void {
159
170
  assert(table.values.count() > 0);
171
+ table.value_count_worst_case = 0;
160
172
  table.values.clearRetainingCapacity();
161
173
  assert(table.values.count() == 0);
162
174
  }
@@ -202,15 +202,16 @@ const Environment = struct {
202
202
 
203
203
  log.debug("forest checkpointing completed!", .{});
204
204
 
205
- var vsr_state = env.superblock.staging.vsr_state;
206
- vsr_state.commit_min += 1;
207
- vsr_state.commit_min_checkpoint += 1;
205
+ const vsr_state = &env.superblock.staging.vsr_state;
208
206
 
209
207
  env.state = .superblock_checkpointing;
210
208
  env.superblock.checkpoint(
211
209
  superblock_checkpoint_callback,
212
210
  &env.superblock_context,
213
- vsr_state,
211
+ .{
212
+ .commit_min_checkpoint = vsr_state.commit_min_checkpoint + 1,
213
+ .commit_min = vsr_state.commit_min + 1,
214
+ },
214
215
  );
215
216
  }
216
217
 
@@ -10,7 +10,7 @@ const log = std.log.scoped(.tree);
10
10
  const tracer = @import("../tracer.zig");
11
11
 
12
12
  const constants = @import("../constants.zig");
13
- const div_ceil = @import("../util.zig").div_ceil;
13
+ const div_ceil = @import("../stdx.zig").div_ceil;
14
14
  const eytzinger = @import("eytzinger.zig").eytzinger;
15
15
  const vsr = @import("../vsr.zig");
16
16
  const bloom_filter = @import("bloom_filter.zig");
@@ -560,7 +560,6 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
560
560
  tree.compact_mutable_table_into_immutable();
561
561
  }
562
562
 
563
- // TODO Defer this callback until tick() to avoid stack growth.
564
563
  callback(tree);
565
564
  return;
566
565
  }