Conversation
|
Welcome to Node.js, and thank you for your first contribution! Before review, please take a moment to read:
Please make sure every commit is signed off. For a first pull request, GitHub Actions require collaborator approval and Jenkins CI must be started by a collaborator or triager, so an initial wait is normal. |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as resolved.
This comment was marked as resolved.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #66088 +/- ##
==========================================
- Coverage 90.28% 90.28% -0.01%
==========================================
Files 790 790
Lines 272043 272059 +16
Branches 51945 51942 -3
==========================================
+ Hits 245619 245631 +12
+ Misses 16931 16930 -1
- Partials 9493 9498 +5
🚀 New features to boost your workflow:
|
49d577d to
f6cd7c6
Compare
|
@MikeMcC399 |
|
@MikeMcC399 |
|
@MikeMcC399 |
As described in the Pull requests > Continuous integration testing guide section, Jenkins CI testing located at https://ci.nodejs.org/ is only accessible to team members. The previous run was started by @inoway46, however I have triggered a re-run of the failing job. If that fails again, it would warrant a closer look, if not, then it was probably a flaky test. |
frame_complete_ alone blocked reset() after a write that had not emitted any bytes yet. ZSTD_reset_session_only cancels unflushed internal data, so refuse only when the incomplete frame has already written output. Refs: nodejs#66088 (comment) Signed-off-by: Xia Chao <shapirolutts@gmail.com>
inoway46
left a comment
There was a problem hiding this comment.
@xia-chao The implementation looks good overall. I left a few inline comments for some final cleanup.
Nit: since commit-queue-squash label squashes into the first commit, the final commit would likely be credited to bun-unsafe. If you'd prefer it to be credited to xia-chao, you may want to squash the commits locally and update the author/sign-off identity before landing.
Resetting a ZstdCompress stream while a frame is still in progress dropped the frame state, but any bytes already written out stayed at the start of the output stream. The next frame was then appended to that fragment, so the resulting stream could not be decompressed. The failure was silent: the compressor reported no error at all. ZSTD_reset_session_only cancels unflushed internal data, so refuse reset only when the incomplete frame has already emitted output. Signed-off-by: Xia Chao <shapirolutts@gmail.com>
4ae01d8 to
2a6a92c
Compare
inoway46
left a comment
There was a problem hiding this comment.
Thanks for addressing the comments. LGTM!
Calling
reset()on a zstd compressor while a frame is still in progress leftthe stream in a state where it produced output that could not be decompressed,
without reporting anything. This makes
reset()throw instead.Fixes #66087.
The problem
reset()drops the state of the frame currently being compressed. Any bytesthat were already written out — by
flush(), or by an earlierwrite()thatfilled the output buffer — cannot be taken back, so they stay at the start of
the output stream. The next frame is then appended to that fragment, and the
result decodes as corruption.
Without the
reset()the same input produces 22 bytes and decodes tohelloworld, becauseend()continues the existing frame (8 bytes) instead ofstarting a new one (14 bytes).
zstd requires buffers to be fully flushed before a new compression job starts —
see
ZSTD_compressStream2indeps/zstd/lib/zstd.h:ZSTD_compressStream2returns non-zero while a frame is unfinished, which ishow a caller is meant to detect this. The compressor context was not keeping
track of that.
The fix
ZstdCompressContextnow records whether the current frame has completed. Aframe is complete once
ZSTD_compressStream2has been called withZSTD_e_endand returned 0.
ResetStream()refuses withERR_ZLIB_INCOMPLETE_FRAMEotherwise:
This follows the existing behaviour for the other invalid reset case, where
reset()during a write already throws.After the change the repro above reports the error instead of emitting a
corrupt stream.
What is not changed
reset()before any write still works.flush()followed byend()still works and still produces a valid stream.reset()on a finished frame is unaffected.Tests
test/parallel/test-zlib-zstd-reset-incomplete-frame.jscovers the resetmid-frame case, the valid
flush()+end()sequence, and the validreset()before writing.Ran against a
--debug-node --debug-symbolsbuild on Linux x86_64:Existing zlib tests:
Notes
gzip and brotli hit the same class of problem for the same call sequence (their
output is undecodable too). I kept this PR to zstd because that is where the
failure is completely silent, but I am happy to look at the other two codecs
separately if that is wanted.