-
Notifications
You must be signed in to change notification settings - Fork 529
Add ZSTD bindings to the node:zlib package. #6007
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add ZSTD bindings to the node:zlib package. #6007
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
|
I have read the CLA Document and I hereby sign the CLA |
src/workerd/api/node/zlib-util.c++
Outdated
| input_.pos = 0; | ||
| output_.dst = output.begin(); | ||
| output_.size = output.size(); | ||
| output_.pos = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could defer to the setInputBuffer and setOutputBuffer methods below to reduce duplication
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
src/workerd/api/node/zlib-util.c++
Outdated
| ZstdEncoderContext::ZstdEncoderContext(ZlibMode _mode): ZstdContext(_mode) { | ||
| cctx_ = ZSTD_createCCtx(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
??
| ZstdEncoderContext::ZstdEncoderContext(ZlibMode _mode): ZstdContext(_mode) { | |
| cctx_ = ZSTD_createCCtx(); | |
| } | |
| ZstdEncoderContext::ZstdEncoderContext(ZlibMode _mode) | |
| : ZstdContext(_mode), | |
| cctx_(ZSTD_createCCtx()) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, should we check for cctx_ == nullptr in here or no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't check for nullptr here, initialize will raise an error if it failed to set. Will update to use the compact style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
src/workerd/api/node/zlib-util.c++
Outdated
| ZstdEncoderContext::~ZstdEncoderContext() { | ||
| if (cctx_ != nullptr) { | ||
| ZSTD_freeCCtx(cctx_); | ||
| cctx_ = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be worth wrapping this in a custom smart pointer to ensure the cleanup is correct. Non-blocking tho.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added (brotli implementation does already do this)
src/workerd/api/node/zlib-util.c++
Outdated
| kj::Maybe<CompressionError> ZstdEncoderContext::initialize(uint64_t pledgedSrcSize) { | ||
| if (cctx_ == nullptr) { | ||
| cctx_ = ZSTD_createCCtx(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When would this be the case since we're calling ZSTD_createCCtx() in the constructor? If there's a possibility this is nullptr here a doc comment explaining the scenario would be helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this was a faulty duplication of some of the behaviour in the brotli implementation; resetStream for brotli calls initialize to reset the context, but zstd has it's own ZSTD_CCtx_reset, so creating the context in initialize is unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cleaned up
src/workerd/api/node/zlib-util.c++
Outdated
| if (ZSTD_isError(result)) { | ||
| error_ = ZSTD_getErrorCode(result); | ||
| return CompressionError(kj::str(ZSTD_getErrorName(result)), | ||
| kj::str("ERR_ZSTD_COMPRESSION_FAILED"), -1); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that this ZSTD_isError(...) block is repeated across multiple functions, it might be worth having a separate utility function, e.g.
KJ_IF_SOME(err, checkIsError(result)) {
return err;
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a zstdCheckError function, although to be honest I'm not sure I like it more than just making the repeated checks 🙈
src/workerd/api/node/zlib-util.c++
Outdated
| } | ||
|
|
||
| kj::Maybe<CompressionError> ZstdEncoderContext::setParams(int key, int value) { | ||
| size_t result = ZSTD_CCtx_setParameter(cctx_, static_cast<ZSTD_cParameter>(key), value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A KJ_DASSERT verifying that key is within the range of ZSTD_cParameter would be helpful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
|
@anonrig ... given your familiarity with the zlib impl in workerd, it would be good to have your review on this as well. |
| constructor(options: ZstdOptions | undefined | null, mode: number) { | ||
| ok(mode === CONST_ZSTD_DECODE || mode === CONST_ZSTD_ENCODE); | ||
| zstdInitParamsArray.fill(-1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This diverges from the Node.js implementation and makes it a lot less readable. Ideally the constructor should receive a parameter for initParamsArray just like Node.js
class Zstd extends ZlibBase {
constructor(opts, mode, initParamsArray, maxParam) {
If we diverge from Node.js, finding bugs & keep the code in sync would be a lot harder then it really is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated 👍
| size = "large", | ||
| src = "zstd-nodejs-test.wd-test", | ||
| args = ["--experimental"], | ||
| data = ["zstd-nodejs-test.js"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| data = ["zstd-nodejs-test.js"], | |
| data = ["zlib-zstd-nodejs-test.js"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed
|
|
||
| wd_test( | ||
| size = "large", | ||
| src = "zstd-nodejs-test.wd-test", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| src = "zstd-nodejs-test.wd-test", | |
| src = "zlib-zstd-nodejs-test.wd-test", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed
| export const zstdConstantsTest = { | ||
| test() { | ||
| // Flush directives | ||
| assert.strictEqual(typeof zlib.constants.ZSTD_e_continue, 'number'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've already tested this in zlib-nodejs-test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed duplicate
|
LGTM. @alistairjevans formatter seems to be failing: https://github.com/cloudflare/workerd/actions/runs/21922721360/job/63307147452?pr=6007 @jasnell can you take a look? |
|
Thanks @anonrig, formatting fix pushed, linter passes locally now. |
|
Overall I think this looks good. What I'm considering... (something I wish I had thought to do on the previous |
Based pretty heavily on the brotli support.
Closes #4013