| ... | @@ -280,7 +280,6 @@ var global_progress: Progress = .{ | ... | @@ -280,7 +280,6 @@ var global_progress: Progress = .{ |
| 280 | .draw_buffer = undefined, | 280 | .draw_buffer = undefined, |
| 281 | .done = false, | 281 | .done = false, |
| 282 | | 282 | |
| 283 | // TODO: make these configurable and avoid including the globals in .data if unused | | |
| 284 | .node_parents = &node_parents_buffer, | 283 | .node_parents = &node_parents_buffer, |
| 285 | .node_storage = &node_storage_buffer, | 284 | .node_storage = &node_storage_buffer, |
| 286 | .node_freelist = &node_freelist_buffer, | 285 | .node_freelist = &node_freelist_buffer, |
| ... | @@ -288,10 +287,10 @@ var global_progress: Progress = .{ | ... | @@ -288,10 +287,10 @@ var global_progress: Progress = .{ |
| 288 | .node_end_index = 0, | 287 | .node_end_index = 0, |
| 289 | }; | 288 | }; |
| 290 | | 289 | |
| 291 | const default_node_storage_buffer_len = 200; | 290 | const node_storage_buffer_len = 200; |
| 292 | var node_parents_buffer: [default_node_storage_buffer_len]Node.Parent = undefined; | 291 | var node_parents_buffer: [node_storage_buffer_len]Node.Parent = undefined; |
| 293 | var node_storage_buffer: [default_node_storage_buffer_len]Node.Storage = undefined; | 292 | var node_storage_buffer: [node_storage_buffer_len]Node.Storage = undefined; |
| 294 | var node_freelist_buffer: [default_node_storage_buffer_len]Node.OptionalIndex = undefined; | 293 | var node_freelist_buffer: [node_storage_buffer_len]Node.OptionalIndex = undefined; |
| 295 | | 294 | |
| 296 | var default_draw_buffer: [4096]u8 = undefined; | 295 | var default_draw_buffer: [4096]u8 = undefined; |
| 297 | | 296 | |
| ... | @@ -391,14 +390,21 @@ fn wait(timeout_ns: u64) bool { | ... | @@ -391,14 +390,21 @@ fn wait(timeout_ns: u64) bool { |
| 391 | } | 390 | } |
| 392 | | 391 | |
| 393 | fn updateThreadRun() void { | 392 | fn updateThreadRun() void { |
| | 393 | // Store this data in the thread so that it does not need to be part of the |
| | 394 | // linker data of the main executable. |
| | 395 | var serialized_buffer: Serialized.Buffer = undefined; |
| | 396 | |
| 394 | { | 397 | { |
| 395 | const resize_flag = wait(global_progress.initial_delay_ns); | 398 | const resize_flag = wait(global_progress.initial_delay_ns); |
| 396 | maybeUpdateSize(resize_flag); | 399 | maybeUpdateSize(resize_flag); |
| 397 | | 400 | |
| 398 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) | 401 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) { |
| | 402 | stderr_mutex.lock(); |
| | 403 | defer stderr_mutex.unlock(); |
| 399 | return clearTerminal(); | 404 | return clearTerminal(); |
| | 405 | } |
| 400 | | 406 | |
| 401 | const buffer = computeRedraw(); | 407 | const buffer = computeRedraw(&serialized_buffer); |
| 402 | if (stderr_mutex.tryLock()) { | 408 | if (stderr_mutex.tryLock()) { |
| 403 | defer stderr_mutex.unlock(); | 409 | defer stderr_mutex.unlock(); |
| 404 | write(buffer); | 410 | write(buffer); |
| ... | @@ -409,10 +415,13 @@ fn updateThreadRun() void { | ... | @@ -409,10 +415,13 @@ fn updateThreadRun() void { |
| 409 | const resize_flag = wait(global_progress.refresh_rate_ns); | 415 | const resize_flag = wait(global_progress.refresh_rate_ns); |
| 410 | maybeUpdateSize(resize_flag); | 416 | maybeUpdateSize(resize_flag); |
| 411 | | 417 | |
| 412 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) | 418 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) { |
| | 419 | stderr_mutex.lock(); |
| | 420 | defer stderr_mutex.unlock(); |
| 413 | return clearTerminal(); | 421 | return clearTerminal(); |
| | 422 | } |
| 414 | | 423 | |
| 415 | const buffer = computeRedraw(); | 424 | const buffer = computeRedraw(&serialized_buffer); |
| 416 | if (stderr_mutex.tryLock()) { | 425 | if (stderr_mutex.tryLock()) { |
| 417 | defer stderr_mutex.unlock(); | 426 | defer stderr_mutex.unlock(); |
| 418 | write(buffer); | 427 | write(buffer); |
| ... | @@ -433,13 +442,17 @@ pub fn unlockStdErr() void { | ... | @@ -433,13 +442,17 @@ pub fn unlockStdErr() void { |
| 433 | } | 442 | } |
| 434 | | 443 | |
| 435 | fn ipcThreadRun(fd: posix.fd_t) anyerror!void { | 444 | fn ipcThreadRun(fd: posix.fd_t) anyerror!void { |
| | 445 | // Store this data in the thread so that it does not need to be part of the |
| | 446 | // linker data of the main executable. |
| | 447 | var serialized_buffer: Serialized.Buffer = undefined; |
| | 448 | |
| 436 | { | 449 | { |
| 437 | _ = wait(global_progress.initial_delay_ns); | 450 | _ = wait(global_progress.initial_delay_ns); |
| 438 | | 451 | |
| 439 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) | 452 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) |
| 440 | return; | 453 | return; |
| 441 | | 454 | |
| 442 | const serialized = serialize(); | 455 | const serialized = serialize(&serialized_buffer); |
| 443 | writeIpc(fd, serialized) catch |err| switch (err) { | 456 | writeIpc(fd, serialized) catch |err| switch (err) { |
| 444 | error.BrokenPipe => return, | 457 | error.BrokenPipe => return, |
| 445 | }; | 458 | }; |
| ... | @@ -451,7 +464,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { | ... | @@ -451,7 +464,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { |
| 451 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) | 464 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) |
| 452 | return clearTerminal(); | 465 | return clearTerminal(); |
| 453 | | 466 | |
| 454 | const serialized = serialize(); | 467 | const serialized = serialize(&serialized_buffer); |
| 455 | writeIpc(fd, serialized) catch |err| switch (err) { | 468 | writeIpc(fd, serialized) catch |err| switch (err) { |
| 456 | error.BrokenPipe => return, | 469 | error.BrokenPipe => return, |
| 457 | }; | 470 | }; |
| ... | @@ -511,17 +524,18 @@ const Children = struct { | ... | @@ -511,17 +524,18 @@ const Children = struct { |
| 511 | sibling: Node.OptionalIndex, | 524 | sibling: Node.OptionalIndex, |
| 512 | }; | 525 | }; |
| 513 | | 526 | |
| 514 | // TODO make this configurable | | |
| 515 | var serialized_node_parents_buffer: [default_node_storage_buffer_len]Node.Parent = undefined; | | |
| 516 | var serialized_node_storage_buffer: [default_node_storage_buffer_len]Node.Storage = undefined; | | |
| 517 | var serialized_node_map_buffer: [default_node_storage_buffer_len]Node.Index = undefined; | | |
| 518 | | | |
| 519 | const Serialized = struct { | 527 | const Serialized = struct { |
| 520 | parents: []Node.Parent, | 528 | parents: []Node.Parent, |
| 521 | storage: []Node.Storage, | 529 | storage: []Node.Storage, |
| | 530 | |
| | 531 | const Buffer = struct { |
| | 532 | parents: [node_storage_buffer_len]Node.Parent, |
| | 533 | storage: [node_storage_buffer_len]Node.Storage, |
| | 534 | map: [node_storage_buffer_len]Node.Index, |
| | 535 | }; |
| 522 | }; | 536 | }; |
| 523 | | 537 | |
| 524 | fn serialize() Serialized { | 538 | fn serialize(serialized_buffer: *Serialized.Buffer) Serialized { |
| 525 | var serialized_len: usize = 0; | 539 | var serialized_len: usize = 0; |
| 526 | var any_ipc = false; | 540 | var any_ipc = false; |
| 527 | | 541 | |
| ... | @@ -533,15 +547,15 @@ fn serialize() Serialized { | ... | @@ -533,15 +547,15 @@ fn serialize() Serialized { |
| 533 | for (node_parents, node_storage, 0..) |*parent_ptr, *storage_ptr, i| { | 547 | for (node_parents, node_storage, 0..) |*parent_ptr, *storage_ptr, i| { |
| 534 | var begin_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst); | 548 | var begin_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst); |
| 535 | while (begin_parent != .unused) { | 549 | while (begin_parent != .unused) { |
| 536 | const dest_storage = &serialized_node_storage_buffer[serialized_len]; | 550 | const dest_storage = &serialized_buffer.storage[serialized_len]; |
| 537 | @memcpy(&dest_storage.name, &storage_ptr.name); | 551 | @memcpy(&dest_storage.name, &storage_ptr.name); |
| 538 | dest_storage.completed_count = @atomicLoad(u32, &storage_ptr.completed_count, .monotonic); | 552 | dest_storage.completed_count = @atomicLoad(u32, &storage_ptr.completed_count, .monotonic); |
| 539 | dest_storage.estimated_total_count = @atomicLoad(u32, &storage_ptr.estimated_total_count, .monotonic); | 553 | dest_storage.estimated_total_count = @atomicLoad(u32, &storage_ptr.estimated_total_count, .monotonic); |
| 540 | const end_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst); | 554 | const end_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst); |
| 541 | if (begin_parent == end_parent) { | 555 | if (begin_parent == end_parent) { |
| 542 | any_ipc = any_ipc or (dest_storage.getIpcFd() != null); | 556 | any_ipc = any_ipc or (dest_storage.getIpcFd() != null); |
| 543 | serialized_node_parents_buffer[serialized_len] = begin_parent; | 557 | serialized_buffer.parents[serialized_len] = begin_parent; |
| 544 | serialized_node_map_buffer[i] = @enumFromInt(serialized_len); | 558 | serialized_buffer.map[i] = @enumFromInt(serialized_len); |
| 545 | serialized_len += 1; | 559 | serialized_len += 1; |
| 546 | break; | 560 | break; |
| 547 | } | 561 | } |
| ... | @@ -551,29 +565,29 @@ fn serialize() Serialized { | ... | @@ -551,29 +565,29 @@ fn serialize() Serialized { |
| 551 | } | 565 | } |
| 552 | | 566 | |
| 553 | // Remap parents to point inside serialized arrays. | 567 | // Remap parents to point inside serialized arrays. |
| 554 | for (serialized_node_parents_buffer[0..serialized_len]) |*parent| { | 568 | for (serialized_buffer.parents[0..serialized_len]) |*parent| { |
| 555 | parent.* = switch (parent.*) { | 569 | parent.* = switch (parent.*) { |
| 556 | .unused => unreachable, | 570 | .unused => unreachable, |
| 557 | .none => .none, | 571 | .none => .none, |
| 558 | _ => |p| serialized_node_map_buffer[@intFromEnum(p)].toParent(), | 572 | _ => |p| serialized_buffer.map[@intFromEnum(p)].toParent(), |
| 559 | }; | 573 | }; |
| 560 | } | 574 | } |
| 561 | | 575 | |
| 562 | // Find nodes which correspond to child processes. | 576 | // Find nodes which correspond to child processes. |
| 563 | if (any_ipc) | 577 | if (any_ipc) |
| 564 | serialized_len = serializeIpc(serialized_len); | 578 | serialized_len = serializeIpc(serialized_len, serialized_buffer); |
| 565 | | 579 | |
| 566 | return .{ | 580 | return .{ |
| 567 | .parents = serialized_node_parents_buffer[0..serialized_len], | 581 | .parents = serialized_buffer.parents[0..serialized_len], |
| 568 | .storage = serialized_node_storage_buffer[0..serialized_len], | 582 | .storage = serialized_buffer.storage[0..serialized_len], |
| 569 | }; | 583 | }; |
| 570 | } | 584 | } |
| 571 | | 585 | |
| 572 | var parents_copy: [default_node_storage_buffer_len]Node.Parent = undefined; | 586 | var parents_copy: [node_storage_buffer_len]Node.Parent = undefined; |
| 573 | var storage_copy: [default_node_storage_buffer_len]Node.Storage = undefined; | 587 | var storage_copy: [node_storage_buffer_len]Node.Storage = undefined; |
| 574 | var ipc_metadata_copy: [default_node_storage_buffer_len]SavedMetadata = undefined; | 588 | var ipc_metadata_copy: [node_storage_buffer_len]SavedMetadata = undefined; |
| 575 | | 589 | |
| 576 | var ipc_metadata: [default_node_storage_buffer_len]SavedMetadata = undefined; | 590 | var ipc_metadata: [node_storage_buffer_len]SavedMetadata = undefined; |
| 577 | var ipc_metadata_len: u16 = 0; | 591 | var ipc_metadata_len: u16 = 0; |
| 578 | | 592 | |
| 579 | const SavedMetadata = struct { | 593 | const SavedMetadata = struct { |
| ... | @@ -597,7 +611,7 @@ const SavedMetadata = struct { | ... | @@ -597,7 +611,7 @@ const SavedMetadata = struct { |
| 597 | } | 611 | } |
| 598 | }; | 612 | }; |
| 599 | | 613 | |
| 600 | fn serializeIpc(start_serialized_len: usize) usize { | 614 | fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buffer) usize { |
| 601 | var serialized_len = start_serialized_len; | 615 | var serialized_len = start_serialized_len; |
| 602 | var pipe_buf: [2 * 4096]u8 align(4) = undefined; | 616 | var pipe_buf: [2 * 4096]u8 align(4) = undefined; |
| 603 | | 617 | |
| ... | @@ -605,8 +619,8 @@ fn serializeIpc(start_serialized_len: usize) usize { | ... | @@ -605,8 +619,8 @@ fn serializeIpc(start_serialized_len: usize) usize { |
| 605 | ipc_metadata_len = 0; | 619 | ipc_metadata_len = 0; |
| 606 | | 620 | |
| 607 | main_loop: for ( | 621 | main_loop: for ( |
| 608 | serialized_node_parents_buffer[0..serialized_len], | 622 | serialized_buffer.parents[0..serialized_len], |
| 609 | serialized_node_storage_buffer[0..serialized_len], | 623 | serialized_buffer.storage[0..serialized_len], |
| 610 | 0.., | 624 | 0.., |
| 611 | ) |main_parent, *main_storage, main_index| { | 625 | ) |main_parent, *main_storage, main_index| { |
| 612 | if (main_parent == .unused) continue; | 626 | if (main_parent == .unused) continue; |
| ... | @@ -628,7 +642,7 @@ fn serializeIpc(start_serialized_len: usize) usize { | ... | @@ -628,7 +642,7 @@ fn serializeIpc(start_serialized_len: usize) usize { |
| 628 | // Ignore all but the last message on the pipe. | 642 | // Ignore all but the last message on the pipe. |
| 629 | var input: []align(2) u8 = pipe_buf[0..bytes_read]; | 643 | var input: []align(2) u8 = pipe_buf[0..bytes_read]; |
| 630 | if (input.len == 0) { | 644 | if (input.len == 0) { |
| 631 | serialized_len = useSavedIpcData(serialized_len, main_storage, main_index, old_ipc_metadata); | 645 | serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata); |
| 632 | continue; | 646 | continue; |
| 633 | } | 647 | } |
| 634 | | 648 | |
| ... | @@ -636,7 +650,7 @@ fn serializeIpc(start_serialized_len: usize) usize { | ... | @@ -636,7 +650,7 @@ fn serializeIpc(start_serialized_len: usize) usize { |
| 636 | if (input.len < 4) { | 650 | if (input.len < 4) { |
| 637 | std.log.warn("short read: {d} out of 4 header bytes", .{input.len}); | 651 | std.log.warn("short read: {d} out of 4 header bytes", .{input.len}); |
| 638 | // TODO keep track of the short read to trash odd bytes with the next read | 652 | // TODO keep track of the short read to trash odd bytes with the next read |
| 639 | serialized_len = useSavedIpcData(serialized_len, main_storage, main_index, old_ipc_metadata); | 653 | serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata); |
| 640 | continue :main_loop; | 654 | continue :main_loop; |
| 641 | } | 655 | } |
| 642 | const subtree_len = std.mem.readInt(u32, input[0..4], .little); | 656 | const subtree_len = std.mem.readInt(u32, input[0..4], .little); |
| ... | @@ -644,7 +658,7 @@ fn serializeIpc(start_serialized_len: usize) usize { | ... | @@ -644,7 +658,7 @@ fn serializeIpc(start_serialized_len: usize) usize { |
| 644 | if (input.len < expected_bytes) { | 658 | if (input.len < expected_bytes) { |
| 645 | std.log.warn("short read: {d} out of {d} ({d} nodes)", .{ input.len, expected_bytes, subtree_len }); | 659 | std.log.warn("short read: {d} out of {d} ({d} nodes)", .{ input.len, expected_bytes, subtree_len }); |
| 646 | // TODO keep track of the short read to trash odd bytes with the next read | 660 | // TODO keep track of the short read to trash odd bytes with the next read |
| 647 | serialized_len = useSavedIpcData(serialized_len, main_storage, main_index, old_ipc_metadata); | 661 | serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata); |
| 648 | continue :main_loop; | 662 | continue :main_loop; |
| 649 | } | 663 | } |
| 650 | if (input.len > expected_bytes) { | 664 | if (input.len > expected_bytes) { |
| ... | @@ -672,12 +686,12 @@ fn serializeIpc(start_serialized_len: usize) usize { | ... | @@ -672,12 +686,12 @@ fn serializeIpc(start_serialized_len: usize) usize { |
| 672 | copyRoot(main_storage, &storage[0]); | 686 | copyRoot(main_storage, &storage[0]); |
| 673 | | 687 | |
| 674 | // Copy the rest of the tree to the end. | 688 | // Copy the rest of the tree to the end. |
| 675 | @memcpy(serialized_node_storage_buffer[serialized_len..][0 .. storage.len - 1], storage[1..]); | 689 | @memcpy(serialized_buffer.storage[serialized_len..][0 .. storage.len - 1], storage[1..]); |
| 676 | | 690 | |
| 677 | // Patch up parent pointers taking into account how the subtree is mounted. | 691 | // Patch up parent pointers taking into account how the subtree is mounted. |
| 678 | serialized_node_parents_buffer[serialized_len] = .none; | 692 | serialized_buffer.parents[serialized_len] = .none; |
| 679 | | 693 | |
| 680 | for (serialized_node_parents_buffer[serialized_len..][0 .. parents.len - 1], parents[1..]) |*dest, p| { | 694 | for (serialized_buffer.parents[serialized_len..][0 .. parents.len - 1], parents[1..]) |*dest, p| { |
| 681 | dest.* = switch (p) { | 695 | dest.* = switch (p) { |
| 682 | // Fix bad data so the rest of the code does not see `unused`. | 696 | // Fix bad data so the rest of the code does not see `unused`. |
| 683 | .none, .unused => .none, | 697 | .none, .unused => .none, |
| ... | @@ -693,8 +707,8 @@ fn serializeIpc(start_serialized_len: usize) usize { | ... | @@ -693,8 +707,8 @@ fn serializeIpc(start_serialized_len: usize) usize { |
| 693 | } | 707 | } |
| 694 | | 708 | |
| 695 | // Save a copy in case any pipes are empty on the next update. | 709 | // Save a copy in case any pipes are empty on the next update. |
| 696 | @memcpy(parents_copy[0..serialized_len], serialized_node_parents_buffer[0..serialized_len]); | 710 | @memcpy(parents_copy[0..serialized_len], serialized_buffer.parents[0..serialized_len]); |
| 697 | @memcpy(storage_copy[0..serialized_len], serialized_node_storage_buffer[0..serialized_len]); | 711 | @memcpy(storage_copy[0..serialized_len], serialized_buffer.storage[0..serialized_len]); |
| 698 | @memcpy(ipc_metadata_copy[0..ipc_metadata_len], ipc_metadata[0..ipc_metadata_len]); | 712 | @memcpy(ipc_metadata_copy[0..ipc_metadata_len], ipc_metadata[0..ipc_metadata_len]); |
| 699 | | 713 | |
| 700 | return serialized_len; | 714 | return serialized_len; |
| ... | @@ -718,6 +732,7 @@ fn findOld(ipc_fd: posix.fd_t, old_metadata: []const SavedMetadata) ?*const Save | ... | @@ -718,6 +732,7 @@ fn findOld(ipc_fd: posix.fd_t, old_metadata: []const SavedMetadata) ?*const Save |
| 718 | | 732 | |
| 719 | fn useSavedIpcData( | 733 | fn useSavedIpcData( |
| 720 | start_serialized_len: usize, | 734 | start_serialized_len: usize, |
| | 735 | serialized_buffer: *Serialized.Buffer, |
| 721 | main_storage: *Node.Storage, | 736 | main_storage: *Node.Storage, |
| 722 | main_index: usize, | 737 | main_index: usize, |
| 723 | old_metadata: []const SavedMetadata, | 738 | old_metadata: []const SavedMetadata, |
| ... | @@ -746,9 +761,9 @@ fn useSavedIpcData( | ... | @@ -746,9 +761,9 @@ fn useSavedIpcData( |
| 746 | | 761 | |
| 747 | copyRoot(main_storage, &storage_copy[old_main_index]); | 762 | copyRoot(main_storage, &storage_copy[old_main_index]); |
| 748 | | 763 | |
| 749 | @memcpy(serialized_node_storage_buffer[start_serialized_len..][0..storage.len], storage); | 764 | @memcpy(serialized_buffer.storage[start_serialized_len..][0..storage.len], storage); |
| 750 | | 765 | |
| 751 | for (serialized_node_parents_buffer[start_serialized_len..][0..parents.len], parents) |*dest, p| { | 766 | for (serialized_buffer.parents[start_serialized_len..][0..parents.len], parents) |*dest, p| { |
| 752 | dest.* = switch (p) { | 767 | dest.* = switch (p) { |
| 753 | .none, .unused => .none, | 768 | .none, .unused => .none, |
| 754 | _ => |prev| @enumFromInt(if (@intFromEnum(prev) == old_main_index) | 769 | _ => |prev| @enumFromInt(if (@intFromEnum(prev) == old_main_index) |
| ... | @@ -761,14 +776,14 @@ fn useSavedIpcData( | ... | @@ -761,14 +776,14 @@ fn useSavedIpcData( |
| 761 | return start_serialized_len + storage.len; | 776 | return start_serialized_len + storage.len; |
| 762 | } | 777 | } |
| 763 | | 778 | |
| 764 | fn computeRedraw() []u8 { | 779 | fn computeRedraw(serialized_buffer: *Serialized.Buffer) []u8 { |
| 765 | const serialized = serialize(); | 780 | const serialized = serialize(serialized_buffer); |
| 766 | | 781 | |
| 767 | // Now we can analyze our copy of the graph without atomics, reconstructing | 782 | // Now we can analyze our copy of the graph without atomics, reconstructing |
| 768 | // children lists which do not exist in the canonical data. These are | 783 | // children lists which do not exist in the canonical data. These are |
| 769 | // needed for tree traversal below. | 784 | // needed for tree traversal below. |
| 770 | | 785 | |
| 771 | var children_buffer: [default_node_storage_buffer_len]Children = undefined; | 786 | var children_buffer: [node_storage_buffer_len]Children = undefined; |
| 772 | const children = children_buffer[0..serialized.parents.len]; | 787 | const children = children_buffer[0..serialized.parents.len]; |
| 773 | | 788 | |
| 774 | @memset(children, .{ .child = .none, .sibling = .none }); | 789 | @memset(children, .{ .child = .none, .sibling = .none }); |