Skip to content

feat(write): add ZipWriter::remove_file for in-place entry deletion - #924

Open
jstorrs wants to merge 2 commits into
zip-rs:masterfrom
jstorrs:feat/remove-file-in-place
Open

feat(write): add ZipWriter::remove_file for in-place entry deletion#924
jstorrs wants to merge 2 commits into
zip-rs:masterfrom
jstorrs:feat/remove-file-in-place

Conversation

@jstorrs

@jstorrs jstorrs commented Aug 11, 2026

Copy link
Copy Markdown

Closes #166.

Adds ZipWriter::remove_file(name), which drops an entry from the archive's
directory without rewriting the archive. finish omits it from the central
directory, so readers no longer see it, while its local header and data are left
in place as unreferenced bytes. Nothing after the removed entry is moved, so the
cost does not scale with the size of the archive — which is what #166 asks for
("rewriting will take way too long on mobile devices").

The file does not shrink. Reclaiming the space still needs a rewrite; the
point is that a caller can now defer and batch that instead of paying it on every
removal. The rustdoc says so and points at merge_archive as one way to compact.

Notes:

  • Only the in-memory directory is mutated — no seeks, no writes — so surviving
    entries keep their offsets and their bytes. An entry from shallow_copy_file
    that shares data with the removed one keeps working; there's a test.
  • shift_remove rather than swap_remove: the central directory is written in
    files order, and reordering the survivors would be a surprising side effect.
    Test included.
  • A file currently being written is finished first, matching start_file.
  • Removing an entry frees its name, so remove-then-add replaces an entry without
    a rewrite. Test included.
  • Panic-free: no indexing, unwrap, or slicing. A missing name is
    ZipError::FileNotFound.

Seven unit tests plus a doctest. Verified locally on Linux against MSRV 1.88,
stable, and nightly, with default / --no-default-features / --all-features,
plus fmt, clippy --all-targets, and doc --no-deps. The remaining axes of
the CI matrix (Windows and macOS) I leave to CI.

jstorrs and others added 2 commits August 11, 2026 17:56
Removes an entry from the archive's directory without rewriting the archive.
The entry stops appearing to readers because `finish` omits it from the
central directory it writes; its local header and data are left in place as
unreferenced bytes, so nothing after the removed entry is moved and the cost
does not scale with the size of the archive.

This is the deletion primitive requested in zip-rs#166, where rewriting was called
out as too slow on mobile devices. The file does not shrink — reclaiming the
space still needs a rewrite, which a caller can now defer and batch instead of
paying on every removal.

The operation only mutates the in-memory directory, so it cannot corrupt
surviving entries: their data is untouched and their offsets are unchanged.
An entry created by `shallow_copy_file` that shares data with the removed one
keeps working for the same reason, and there is a test for it.

`shift_remove` rather than `swap_remove`, since the central directory is
written in `files` order and reordering the survivors would be a surprising
side effect of a removal. A file currently being written is finished first,
matching what `start_file` does.

Freeing the name also makes replace-without-rewrite possible: remove, then
add the same name again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

Deletion of zip entries in-place

1 participant