Skip to content

feat: lazily allocate FileContent chunks, sparse until first write - #164

Merged
coldhighsun merged 1 commit into
mainfrom
feature/sparse-file-content
Sep 16, 2026
Merged

coldhighsun merged 1 commit into
mainfrom
feature/sparse-file-content

Conversation

@coldhighsun

Copy link
Copy Markdown
Owner

Summary

  • FileContent's chunk list (List<byte[]>) becomes List<byte[]?>. Growth (CreateZeroed/Resize) no longer eagerly new byte[]s every chunk it now spans — an untouched chunk stays null (sparse), since the whole point of growth is exposing more zero bytes and a null entry already reads as zero.
  • Read-only paths (ReadTo, CopyTo, HashInto, ToArray, ChunkReadStream.Read) satisfy a sparse chunk from a shared static all-zero buffer without allocating.
  • Write paths (WriteFrom, WriteManaged, FillFromStream) materialize a chunk (via a new AllocateChunk helper, right-sized for the terminal chunk) only the first time something is actually written into it.
  • Clone() preserves sparseness: a chunk the source never wrote to stays sparse in the clone too, instead of eagerly copying/materializing it.
  • Capacity/quota accounting (FileNodeMap.GetTotalAllocated, the low-memory guard from feat: reject RAM disk growth when system memory runs low #163) is unaffected — both already key off configured/logical allocation size, not materialized chunk bytes, so nothing needed to change there.
  • Scope: in-memory only. .mdr image save/load and snapshot blobs still serialize full byte ranges (no on-disk hole/sparse encoding), so a reloaded file re-materializes fully — the memory saving applies within a single mount session, mainly to freshly created, extended, or truncated-then-regrown regions that are never actually written.

Test plan

  • dotnet build -c Release — 0 warnings, 0 errors
  • dotnet test tests/ManagedDrive.Tests -c Release --filter "FullyQualifiedName~FileContentTests" — 22/22 passed (3 existing tests updated since they previously asserted a freshly created file is already fully materialized; ~6 new tests added covering sparse creation, lazy materialization on write, sparse reads, growth-across-chunk-boundary, and clone sparseness)
  • dotnet test tests/ManagedDrive.Tests -c Release — 380/380 passed
  • dotnet format --verify-no-changes --no-restore — clean

Growing a file (create, resize, extend) used to eagerly new byte[] every
chunk it now spans, even though the whole point of growth is to expose
more zero bytes. Represent an untouched chunk as null instead and let
read paths (ReadTo/CopyTo/HashInto/ToArray/ChunkReadStream) satisfy it
from a shared static zero buffer without allocating; a chunk only
materializes the first time WriteFrom/WriteManaged/FillFromStream
actually writes into it. Clone() preserves sparseness for chunks the
source never wrote to.

This only affects in-memory representation - persisted .mdr images and
snapshot blobs still serialize full byte ranges, so a reloaded file
re-materializes fully; the memory saving applies within a single mount
session for freshly created/extended/truncated regions.
@coldhighsun
coldhighsun merged commit 9864cad into main Sep 16, 2026
4 checks passed
@coldhighsun
coldhighsun deleted the feature/sparse-file-content branch September 16, 2026 10:00
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