Skip to content

Zstd compression overrides, parallel decompression, and misc fixes - #128

Merged
coldhighsun merged 14 commits into
mainfrom
feature/mount-perf-and-compression-updates
Aug 1, 2026
Merged

Zstd compression overrides, parallel decompression, and misc fixes#128
coldhighsun merged 14 commits into
mainfrom
feature/mount-perf-and-compression-updates

Conversation

@coldhighsun

Copy link
Copy Markdown
Owner

Summary

  • Add custom Zstd compression level override support for mdrive mount
  • Decompress .mdr node region in parallel across Zstd chunks for faster loads
  • Fix manual-save progress halving when no snapshot write follows
  • Fix suppressed terminal 1.0 progress report in BusyOverlayViewModel
  • Various perf improvements: 1MB sequential-scan FileStream buffers, SortedSet range query for directory children, deduped UtcNow calls with cached content hashes for snapshot diffing
  • Throttle high-frequency log sites via ThrottledLogging
  • Refactor: extract node-metadata IO / stateless helpers, dedupe boilerplate across MainViewModel/RamDisk/serialization
  • Complete third-party notices, link from About dialog; update README benchmark numbers
  • IDE code cleanup (member ordering, minor doc fixes)

Test plan

  • dotnet build succeeds with 0 warnings/errors
  • dotnet test tests/ManagedDrive.Tests (run in CI)
  • Manual smoke test: mount/unmount, custom compression level, save/restore snapshot

Log a throttled warning when a disk's high-usage warning fires, and
apply the same package to other repeat-failure log sites (auto-save
failures, snapshot pruning/blob GC, helper service reconciliation and
pipe errors) so a persistent condition doesn't spam the log file.
… serialization

Consolidates repeated path-collision checks, MessageBox boilerplate, compression-level
mapping, and legacy-image-header parsing into shared helpers; fixes a UI-thread stall
where editing a disk's password during a remount ran SetPassword synchronously outside
the Task.Run that wraps the mount; and adds a round-trip test for the
DiskOptions/DiskProfile mapping.
Consolidates the identical path/FileInfo/security-descriptor read-write logic
shared by DiskImageSerializer and SnapshotStore into NodeMetadataIO, and moves
MainViewModel's stateless helper methods into MainViewModelHelpers; documents
the CLI adapter's exact contract surface with MainViewModel.
…hes for snapshot diffing

Write previously took DateTimeOffset.UtcNow three times per call (metadata
timestamps, MarkDirty, content-access tracking); it now captures one
timestamp and reuses it. FileNode gains a ContentVersion counter bumped
only when content bytes actually change (Write/Overwrite/SetFileSizeCore),
letting SnapshotManager.ComputeHash cache a file's SHA-256 across repeated
diff/GC passes instead of rehashing unchanged files on every dirty
auto-save tick.
GetChildren (used by CanDelete on every directory-delete check) scanned
the whole sorted namespace from the start to find where a path prefix's
run began. FileNodeMap now keeps a plain Dictionary for O(1) node lookup
alongside a SortedSet<string> of keys, and GetChildren/RenameDescendants
seek directly into the prefix's range via GetViewBetween in O(log n)
instead of a full-map scan.
Save/Load/PeekHeader open the .mdr file with the default 4KB buffer,
causing many small syscalls when writing per-node metadata directly
to the stream (most noticeable on uncompressed images, where there's
no GZipStream buffering in front of it).
…ewModel

The epsilon check meant to dedupe near-identical intermediate ticks could
also swallow a final Report(1.0) that landed within epsilon of the last
stored value, leaving the progress bar visibly stuck just short of 100%.
SaveToImageWithSnapshot always split progress into [0, 0.5] for the image
save and [0.5, 1.0] for the snapshot write, even when snapshot retention
isn't configured. TryWriteSnapshot then no-ops and jumps straight to 1.0,
so the bar visibly stalled around 50% before snapping to done. Now the
full [0, 1] range goes to the image save unless a snapshot write may
actually happen.
Frame each compressed chunk as [Int32 length][bytes] (mirroring
ChunkedGcm's framing) instead of relying on transparent concatenated
Zstd frames, so load can dispatch each chunk's decompression to a
worker pool the same way save already parallelizes compression.
SnapshotStore's blob reader picks up the same ParallelZstd.ReadStream
since it shares the writer.
Let a disk's save/snapshot compression optionally override the exact
Zstd level (1-22) instead of the coarse Fastest/Optimal/SmallestSize
mapping, exposed as an advanced option in CreateDiskDialog and
persisted per-disk via DiskProfile/DiskOptions. Bumps SharpCompress
to 0.50.3.
List every distributed NuGet dependency (ZstdSharp.Port, ThrottledLogging,
System.CommandLine, Spectre.Console, YamlDotNet, Microsoft.Extensions.*,
Serilog and its sinks) in THIRD-PARTY-NOTICES.md, and replace the
per-package WinFsp/SharpCompress hyperlinks in AboutDialog with a single
link to that file so future dependency additions only require a doc
update.
Wires DiskOptions.CustomZstdLevel through the CLI mount path (CliMountOverrides,
MountOptionsFactory.MountOverrides, and MainViewModel.MountImageAsync) so
--custom-zstd-level (1-22) can override the preset level mapped from
--compression, matching the GUI's existing custom-level support.
@coldhighsun
coldhighsun merged commit 7ee8bc4 into main Aug 1, 2026
3 checks passed
@coldhighsun
coldhighsun deleted the feature/mount-perf-and-compression-updates branch August 1, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant