feat: optional zstd dictionary binary-diff backend - #110
Conversation
|
cc: @alexlarsson |
|
I think this code is merge ready. Is this waiting on the testing plan in the description to be completed? |
|
no, this is all done |
|
Other than the code comments above, there are some highlevel issues. First of all, I'm not sure that the options are set up the right way, as it implies you can only have bsdiff or only zstd dict chunks. bsdiff is more costly, but it does allow fully streaming apply, which means it is more realistic to use for larger files. I can imagine maybe using zstd for smaller files and bsdiff where needed for larger files. So, these should maybe use different max sizes? Also, I don't think we can just add a new delta op type without making this versioned in the file format. Existing patch apply codebases (like old tar-diff, or the reimplementations in composefs-rs and flatpak) will just die with some unknown delta op error. I think we need to add a version field to the format. I think we should do: And emit the second when there is any zstd op in the file. |
|
I also tried re-implementing the apply path in C, and there is no stable api support for WithDecoderDictRaw, instead we have to rely on ZSTD_dct_auto, which checks the header of of the dict, and unless it starts with the zstd dictionary magic (0xEC30A437) word then it assumes raw. I think it is unlikely that we hit this, but can we maybe refuse to use zstd ops for files that happens to start with this prefix? |
|
Addressed the review in 49b2d52: Format. v2 magic Options. Apply/encode. Dict still has to be fully in memory ( |
Ignore this, i found a way to avoid the issue in C. |
|
That case is what For a generator that wants mostly zstd but still a streaming delta on the huge files:
|
Allow per-file zstd --patch-from patches (--binary-diff bsdiff|zstd|auto) with separate size caps. Deltas that can emit ZstdDict use tardf2 up front. Cap the zstd window at min(512MiB, zstd.MaxWindowSize) so older apply stays compatible if klauspost raises MaxWindowSize later. Signed-off-by: Asaf Ben Natan <asafbennatan@gmail.com> Made-with: Cursor
45acd19 to
d88e266
Compare
|
Squashed the branch to a single commit. Zstd window is now |
|
Locally, I'm getting this: Also, we're getting weird CI issues: |
Apply reads the whole source as the dictionary, so later reuse of that file must emit Seek(0). Bump golangci-lint to v2.13.1 for Go 1.27. Signed-off-by: Asaf Ben Natan <asafbennatan@gmail.com> Made-with: Cursor
|
I think there is a remaining issue with the fallback in generateForFileWithZstd(). It just copies the raw data to the stream, but it needs to use WriteContent() or equivalent, because otherwise we're not getting a DeltaOpData header for the data. Also, it think there is a problem with WriteContent() in that it doesn't correctly handle buffers larger than deltaDataChunkSize(). It needs to loop and potentially generate multiple chunks in that case. |
WriteContent now splits at 4MiB so a large raw fallback (or steal copy) never emits one oversized data op. Signed-off-by: Asaf Ben Natan <asafbennatan@gmail.com> Made-with: Cursor
Flush a full chunk after the loop so we do not return early while data is still being copied. Signed-off-by: Asaf Ben Natan <asafbennatan@gmail.com> Made-with: Cursor
|
🎉 This PR is included in version 0.6.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
--patch-fromstyle), selectable via--binary-diff zstd(default remainsbsdiff).--compression-levelfor the outer delta stream and per-file patches; add--zstd-diff-level,--zstd-diff-window, and--zstd-diff-fallback-rawknobs for tuning dictionary patch quality and size.file-format.md.Test plan
go test ./pkg/tar-diff/... ./pkg/tar-patch/...tar-diffwith--binary-diff zstdon representative image layers and confirm smaller deltas vs bsdiff where expected.tardiffwithtar-patchand verify output matches the new tarbsdiffbehavior is unchangedfollows the discussion here:
containers/oci-delta#65